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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to