Author: violetagg
Date: Tue Sep 16 20:07:55 2014
New Revision: 1625368
URL: http://svn.apache.org/r1625368
Log:
Merged revisions 1604822,1604833 from tomcat/trunk:
- Add plumbing to enable permessage-deflate implementation
- This is still a work-in-progress
- Trivial format change
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/WsFrameBase.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameClient.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsSession.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsFrameServer.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
Merged /tomcat/trunk:r1604822
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=1625368&r1=1625367&r2=1625368&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:07:55 2014
@@ -57,7 +57,7 @@ wsFrame.notMasked=The client frame was n
wsFrame.oneByteCloseCode=The client sent a close frame with a single byte
payload which is not valid
wsFrame.sessionClosed=The client data can not be processed because the session
has already been closed
wsFrame.textMessageTooBig=The decoded text message was too big for the output
buffer and the endpoint does not support partial messages
-wsFrame.wrongRsv=The client frame set the reserved bits to [{0}] which was not
supported by this endpoint
+wsFrame.wrongRsv=The client frame set the reserved bits to [{0}] for a message
with opCode [{1}] which was not supported by this endpoint
wsRemoteEndpoint.closed=Message will not be sent because the WebSocket session
has been closed
wsRemoteEndpoint.closedDuringMessage=The remainder of the message will not be
sent because the WebSocket session has been closed
Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java?rev=1625368&r1=1625367&r2=1625368&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java
(original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java Tue
Sep 16 20:07:55 2014
@@ -46,6 +46,7 @@ public abstract class WsFrameBase {
// Connection level attributes
protected final WsSession wsSession;
protected final byte[] inputBuffer;
+ private final Transformation transformation;
// Attributes for control messages
// Control messages can appear in the middle of other messages so need
@@ -84,14 +85,25 @@ public abstract class WsFrameBase {
private int readPos = 0;
protected int writePos = 0;
- public WsFrameBase(WsSession wsSession) {
-
+ public WsFrameBase(WsSession wsSession, Transformation transformation) {
inputBuffer = new byte[Constants.DEFAULT_BUFFER_SIZE];
messageBufferBinary =
ByteBuffer.allocate(wsSession.getMaxBinaryMessageBufferSize());
messageBufferText =
CharBuffer.allocate(wsSession.getMaxTextMessageBufferSize());
this.wsSession = wsSession;
+ Transformation finalTransformation;
+ if (isMasked()) {
+ finalTransformation = new UnmaskTransformation();
+ } else {
+ finalTransformation = new NoopTransformation();
+ }
+ if (transformation == null) {
+ this.transformation = finalTransformation;
+ } else {
+ transformation.setNext(finalTransformation);
+ this.transformation = transformation;
+ }
}
@@ -134,14 +146,14 @@ public abstract class WsFrameBase {
int b = inputBuffer[readPos++];
fin = (b & 0x80) > 0;
rsv = (b & 0x70) >>> 4;
- if (rsv != 0) {
- // Note extensions may use rsv bits but currently no extensions are
- // supported
+ opCode = (byte) (b & 0x0F);
+ if (!transformation.validateRsv(rsv, opCode)) {
throw new WsIOException(new CloseReason(
CloseCodes.PROTOCOL_ERROR,
- sm.getString("wsFrame.wrongRsv", Integer.valueOf(rsv))));
+ sm.getString("wsFrame.wrongRsv", Integer.valueOf(rsv),
+ Integer.valueOf(opCode))));
}
- opCode = (byte) (b & 0x0F);
+
if (Util.isControl(opCode)) {
if (!fin) {
throw new WsIOException(new CloseReason(
@@ -288,7 +300,7 @@ public abstract class WsFrameBase {
private boolean processDataControl() throws IOException {
- if (!appendPayloadToMessage(controlBufferBinary)) {
+ if (!transformation.getMoreData(opCode, rsv, controlBufferBinary)) {
return false;
}
controlBufferBinary.flip();
@@ -386,7 +398,7 @@ public abstract class WsFrameBase {
private boolean processDataText() throws IOException {
// Copy the available data to the buffer
- while (!appendPayloadToMessage(messageBufferBinary)) {
+ while (!transformation.getMoreData(opCode, rsv, messageBufferBinary)) {
// Frame not complete - we ran out of something
// Convert bytes to UTF-8
messageBufferBinary.flip();
@@ -481,7 +493,7 @@ public abstract class WsFrameBase {
private boolean processDataBinary() throws IOException {
// Copy the available data to the buffer
- while (!appendPayloadToMessage(messageBufferBinary)) {
+ while (!transformation.getMoreData(opCode, rsv, messageBufferBinary)) {
// Frame not complete - what did we run out of?
if (readPos == writePos) {
// Ran out of input data - get some more
@@ -630,34 +642,6 @@ public abstract class WsFrameBase {
}
- private boolean appendPayloadToMessage(ByteBuffer dest) {
- if (isMasked()) {
- while (payloadWritten < payloadLength && readPos < writePos &&
- dest.hasRemaining()) {
- byte b = (byte) ((inputBuffer[readPos] ^ mask[maskIndex]) &
0xFF);
- maskIndex++;
- if (maskIndex == 4) {
- maskIndex = 0;
- }
- readPos++;
- payloadWritten++;
- dest.put(b);
- }
- return (payloadWritten == payloadLength);
- } else {
- long toWrite = Math.min(
- payloadLength - payloadWritten, writePos - readPos);
- toWrite = Math.min(toWrite, dest.remaining());
-
- dest.put(inputBuffer, readPos, (int) toWrite);
- readPos += toWrite;
- payloadWritten += toWrite;
- return (payloadWritten == payloadLength);
-
- }
- }
-
-
private boolean swallowInput() {
long toSkip = Math.min(payloadLength - payloadWritten, writePos -
readPos);
readPos += toSkip;
Modified:
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameClient.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameClient.java?rev=1625368&r1=1625367&r2=1625368&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameClient.java
(original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsFrameClient.java
Tue Sep 16 20:07:55 2014
@@ -32,7 +32,8 @@ public class WsFrameClient extends WsFra
public WsFrameClient(ByteBuffer response, AsyncChannelWrapper channel,
WsSession wsSession) {
- super(wsSession);
+ // TODO Add support for extensions to the client side code
+ super(wsSession, null);
this.response = response;
this.channel = channel;
this.handler = new WsFrameClientCompletionHandler();
Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsSession.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsSession.java?rev=1625368&r1=1625367&r2=1625368&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsSession.java
(original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsSession.java Tue
Sep 16 20:07:55 2014
@@ -114,8 +114,8 @@ public class WsSession implements Sessio
URI requestUri, Map<String,List<String>> requestParameterMap,
String queryString, Principal userPrincipal, String httpSessionId,
String subProtocol, Map<String,String> pathParameters,
- boolean secure, EndpointConfig endpointConfig)
- throws DeploymentException {
+ boolean secure, EndpointConfig endpointConfig,
+ Transformation transformation) throws DeploymentException {
this.localEndpoint = localEndpoint;
this.wsRemoteEndpoint = wsRemoteEndpoint;
this.wsRemoteEndpoint.setSession(this);
Modified:
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java?rev=1625368&r1=1625367&r2=1625368&view=diff
==============================================================================
---
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
(original)
+++
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
Tue Sep 16 20:07:55 2014
@@ -352,7 +352,7 @@ public class WsWebSocketContainer
WsSession wsSession = new WsSession(endpoint, wsRemoteEndpointClient,
this, null, null, null, null, null, subProtocol,
Collections.<String, String> emptyMap(), secure,
- clientEndpointConfiguration);
+ clientEndpointConfiguration, null);
endpoint.onOpen(wsSession, clientEndpointConfiguration);
registerSession(endpoint, wsSession);
Modified:
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java?rev=1625368&r1=1625367&r2=1625368&view=diff
==============================================================================
---
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
(original)
+++
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
Tue Sep 16 20:07:55 2014
@@ -17,7 +17,9 @@
package org.apache.tomcat.websocket.server;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.websocket.Extension;
import javax.websocket.HandshakeResponse;
@@ -56,10 +58,13 @@ public class DefaultServerEndpointConfig
@Override
public List<Extension> getNegotiatedExtensions(List<Extension> installed,
List<Extension> requested) {
-
+ Set<String> installedNames = new HashSet<String>();
+ for (Extension e : installed) {
+ installedNames.add(e.getName());
+ }
List<Extension> result = new ArrayList<Extension>();
for (Extension request : requested) {
- if (installed.contains(request)) {
+ if (installedNames.contains(request.getName())) {
result.add(request);
}
}
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=1625368&r1=1625367&r2=1625368&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:07:55 2014
@@ -21,9 +21,7 @@ import java.nio.charset.StandardCharsets
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Enumeration;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -44,6 +42,9 @@ import javax.websocket.server.ServerEndp
import org.apache.catalina.connector.RequestFacade;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.websocket.Constants;
+import org.apache.tomcat.websocket.Transformation;
+import org.apache.tomcat.websocket.TransformationFactory;
+import org.apache.tomcat.websocket.Util;
import org.apache.tomcat.websocket.WsHandshakeResponse;
import org.apache.tomcat.websocket.pojo.PojoEndpointServer;
@@ -88,7 +89,6 @@ public class UpgradeUtil {
// validation fails
String key;
String subProtocol = null;
- List<Extension> extensions = Collections.emptyList();
if (!headerContainsToken(req, Constants.CONNECTION_HEADER_NAME,
Constants.CONNECTION_HEADER_VALUE)) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
@@ -121,7 +121,42 @@ public class UpgradeUtil {
sec.getSubprotocols(), subProtocols);
// Extensions
- // Currently no extensions are supported by this implementation
+ // Should normally only be one header but handle the case of multiple
+ // headers
+ List<Extension> extensionsRequested = new ArrayList<Extension>();
+ Enumeration<String> extHeaders =
req.getHeaders("Sec-WebSocket-Extensions");
+ while (extHeaders.hasMoreElements()) {
+ Util.parseExtensionHeader(extensionsRequested,
extHeaders.nextElement());
+ }
+ List<Extension> negotiatedExtensions =
sec.getConfigurator().getNegotiatedExtensions(
+ Constants.INSTALLED_EXTENSIONS, extensionsRequested);
+
+ // Create the Transformations that will be applied to this connection
+ List<Transformation> transformations =
createTransformations(negotiatedExtensions);
+
+ // Build the transformation pipeline
+ Transformation transformation = null;
+ StringBuilder responseHeaderExtensions = new StringBuilder();
+ boolean first = true;
+ for (Transformation t : transformations) {
+ if (first) {
+ first = false;
+ } else {
+ responseHeaderExtensions.append(',');
+ }
+ append(responseHeaderExtensions, t.getExtensionResponse());
+ if (transformation == null) {
+ transformation = t;
+ } else {
+ transformation.setNext(t);
+ }
+ }
+
+ // Now we have the full pipeline, validate the use of the RSV bits.
+ if (transformation != null && !transformation.validateRsvBits(0)) {
+ // TODO i18n
+ throw new ServletException("Incompatible RSV bit usage");
+ }
// If we got this far, all is good. Accept the connection.
resp.setHeader(Constants.UPGRADE_HEADER_NAME,
@@ -134,16 +169,8 @@ public class UpgradeUtil {
// RFC6455 4.2.2 explicitly states "" is not valid here
resp.setHeader("Sec-WebSocket-Protocol", subProtocol);
}
- if (!extensions.isEmpty()) {
- StringBuilder sb = new StringBuilder();
- Iterator<Extension> iter = extensions.iterator();
- // There must be at least one
- sb.append(iter.next());
- while (iter.hasNext()) {
- sb.append(',');
- sb.append(iter.next().getName());
- }
- resp.setHeader("Sec-WebSocket-Extensions", sb.toString());
+ if (!transformations.isEmpty()) {
+ resp.setHeader("Sec-WebSocket-Extensions",
responseHeaderExtensions.toString());
}
WsHandshakeRequest wsRequest = new WsHandshakeRequest(req);
@@ -185,13 +212,44 @@ public class UpgradeUtil {
WsHttpUpgradeHandler wsHandler =
((RequestFacade)
inner).upgrade(WsHttpUpgradeHandler.class);
wsHandler.preInit(ep, perSessionServerEndpointConfig, sc,
wsRequest,
- subProtocol, pathParams, req.isSecure());
+ subProtocol, transformation, pathParams, req.isSecure());
} else {
throw new ServletException("Upgrade failed");
}
}
+ private static List<Transformation> createTransformations(
+ List<Extension> negotiatedExtensions) {
+
+ TransformationFactory factory = TransformationFactory.getInstance();
+
+ List<Transformation> result = new
ArrayList<Transformation>(negotiatedExtensions.size());
+
+ for (Extension extension : negotiatedExtensions) {
+ result.add(factory.create(extension));
+ }
+ return result;
+ }
+
+ private static void append(StringBuilder sb, Extension extension) {
+ if (extension == null || extension.getName() == null ||
extension.getName().length() == 0) {
+ return;
+ }
+
+ sb.append(extension.getName());
+
+ for (Extension.Parameter p : extension.getParameters()) {
+ sb.append(';');
+ sb.append(p.getName());
+ if (p.getValue() != null) {
+ sb.append('=');
+ sb.append(p.getValue());
+ }
+ }
+ }
+
+
/*
* This only works for tokens. Quoted strings need more sophisticated
* parsing.
Modified:
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsFrameServer.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsFrameServer.java?rev=1625368&r1=1625367&r2=1625368&view=diff
==============================================================================
---
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsFrameServer.java
(original)
+++
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsFrameServer.java
Tue Sep 16 20:07:55 2014
@@ -20,6 +20,7 @@ import java.io.EOFException;
import java.io.IOException;
import org.apache.coyote.http11.upgrade.AbstractServletInputStream;
+import org.apache.tomcat.websocket.Transformation;
import org.apache.tomcat.websocket.WsFrameBase;
import org.apache.tomcat.websocket.WsSession;
@@ -29,8 +30,9 @@ public class WsFrameServer extends WsFra
private final Object connectionReadLock = new Object();
- public WsFrameServer(AbstractServletInputStream sis, WsSession wsSession) {
- super(wsSession);
+ public WsFrameServer(AbstractServletInputStream sis, WsSession wsSession,
+ Transformation transformation) {
+ super(wsSession, transformation);
this.sis = sis;
}
Modified:
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java?rev=1625368&r1=1625367&r2=1625368&view=diff
==============================================================================
---
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
(original)
+++
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
Tue Sep 16 20:07:55 2014
@@ -36,6 +36,7 @@ import org.apache.coyote.http11.upgrade.
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.websocket.Transformation;
import org.apache.tomcat.websocket.WsIOException;
import org.apache.tomcat.websocket.WsSession;
@@ -56,6 +57,7 @@ public class WsHttpUpgradeHandler implem
private WsServerContainer webSocketContainer;
private WsHandshakeRequest handshakeRequest;
private String subProtocol;
+ private Transformation transformation;
private Map<String,String> pathParameters;
private boolean secure;
private WebConnection connection;
@@ -70,13 +72,14 @@ public class WsHttpUpgradeHandler implem
public void preInit(Endpoint ep, EndpointConfig endpointConfig,
WsServerContainer wsc, WsHandshakeRequest handshakeRequest,
- String subProtocol, Map<String,String> pathParameters,
- boolean secure) {
+ String subProtocol, Transformation transformation,
+ Map<String,String> pathParameters, boolean secure) {
this.ep = ep;
this.endpointConfig = endpointConfig;
this.webSocketContainer = wsc;
this.handshakeRequest = handshakeRequest;
this.subProtocol = subProtocol;
+ this.transformation = transformation;
this.pathParameters = pathParameters;
this.secure = secure;
}
@@ -120,12 +123,9 @@ public class WsHttpUpgradeHandler implem
handshakeRequest.getParameterMap(),
handshakeRequest.getQueryString(),
handshakeRequest.getUserPrincipal(), httpSessionId,
- subProtocol, pathParameters, secure, endpointConfig);
- WsFrameServer wsFrame = new WsFrameServer(
- sis,
- wsSession);
- sos.setWriteListener(
- new WsWriteListener(this, wsRemoteEndpointServer));
+ subProtocol, pathParameters, secure, endpointConfig,
transformation);
+ WsFrameServer wsFrame = new WsFrameServer(sis, wsSession,
transformation);
+ sos.setWriteListener(new WsWriteListener(this,
wsRemoteEndpointServer));
ep.onOpen(wsSession, endpointConfig);
webSocketContainer.registerSession(ep, wsSession);
sis.setReadListener(new WsReadListener(this, wsFrame));
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]