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 2012/03/16 00:11:48 UTC

svn commit: r1301263 - in /tomcat/tc7.0.x/trunk: ./ java/org/apache/catalina/util/ java/org/apache/catalina/websocket/ java/org/apache/coyote/ java/org/apache/coyote/http11/upgrade/ test/org/apache/catalina/util/ test/org/apache/catalina/websocket/ web...

Author: markt
Date: Thu Mar 15 23:11:47 2012
New Revision: 1301263

URL: http://svn.apache.org/viewvc?rev=1301263&view=rev
Log:
More WebSocket changes including some code based on ideas in a larger patch by Petr Praus, Jonathan Drake & Slávka

Added:
    tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java
      - copied, changed from r1245848, tomcat/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java
    tomcat/tc7.0.x/trunk/test/org/apache/catalina/util/TestConversions.java
      - copied unchanged from r1245848, tomcat/trunk/test/org/apache/catalina/util/TestConversions.java
Modified:
    tomcat/tc7.0.x/trunk/   (props changed)
    tomcat/tc7.0.x/trunk/java/org/apache/catalina/util/Conversions.java
    tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java
    tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/MessageInbound.java
    tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java
    tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java
    tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java
    tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java
    tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java
    tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/Constants.java
    tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java
    tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java
    tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java
    tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java
    tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java
    tomcat/tc7.0.x/trunk/test/org/apache/catalina/websocket/TestWebSocket.java   (contents, props changed)
    tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java   (contents, props changed)
    tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java   (contents, props changed)
    tomcat/tc7.0.x/trunk/webapps/examples/websocket/index.html

Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Mar 15 23:11:47 2012
@@ -1 +1 @@
-/tomcat/trunk:1156115-1157160,1157162-1157859,1157862-1157942,1157945-1160347,1160349-1163716,1163718-1166689,1166691-1174340,1174342-1175596,1175598-1175611,1175613-1175932,1175934-1177783,1177785-1177980,1178006-1180720,1180722-1183094,1183096-1187753,1187755,1187775,1187801,1187806,1187809,1187826-1188312,1188314-1188401,1188646-1188840,1188842-1190176,1190178-1195223,1195225-1195953,1195955,1195957-1201238,1201240-1203345,1203347-1206623,1206625-1208046,1208073,1208096,1208114,1208145,1208772,1209194-1212125,1212127-1220291,1220293,1220295-1221321,1221323-1222328,1222332-1222401,1222405-1222795,1222850-1222950,1222969-1225326,1225328-1225463,1225465,1225627,1225629-1226534,1226536-1228908,1228911-1228923,1228927-1229532,1229534-1230766,1230768-1231625,1231627-1233414,1233419-1235207,1235209-1237425,1237427,1237429-1237977,1237981,1237985,1237995,1238070,1238073,1239024-1239048,1239050-1239062,1239135,1239256,1239258-1239485,1239785-1240046,1240101,1240106,1240109,1240112
 ,1240114,1240116,1240118,1240121,1240329,1240474-1240850,1240857,1241087,1241160,1241408-1241822,1241908-1241909,1241982,1242099,1242110,1242371,1242434,1242495,1242947,1243034,1243038,1244302,1244511,1244567,1244718-1244719,1244935-1244938,1245274,1245449,1245849,1290875,1292334,1292338,1292345-1292347,1293155,1293831-1293832,1295998,1296284,1297014-1297015,1297017,1297158,1297177,1297202,1297209,1297213,1297717,1297722,1297729,1297768,1297778,1297818,1297828,1297979,1297987,1298121,1298140,1298590,1298592,1298628-1298629,1298794,1298983-1298984,1299020,1299034,1299819,1300154-1300155,1300569,1300948
+/tomcat/trunk:1156115-1157160,1157162-1157859,1157862-1157942,1157945-1160347,1160349-1163716,1163718-1166689,1166691-1174340,1174342-1175596,1175598-1175611,1175613-1175932,1175934-1177783,1177785-1177980,1178006-1180720,1180722-1183094,1183096-1187753,1187755,1187775,1187801,1187806,1187809,1187826-1188312,1188314-1188401,1188646-1188840,1188842-1190176,1190178-1195223,1195225-1195953,1195955,1195957-1201238,1201240-1203345,1203347-1206623,1206625-1208046,1208073,1208096,1208114,1208145,1208772,1209194-1212125,1212127-1220291,1220293,1220295-1221321,1221323-1222328,1222332-1222401,1222405-1222795,1222850-1222950,1222969-1225326,1225328-1225463,1225465,1225627,1225629-1226534,1226536-1228908,1228911-1228923,1228927-1229532,1229534-1230766,1230768-1231625,1231627-1233414,1233419-1235207,1235209-1237425,1237427,1237429-1237977,1237981,1237985,1237995,1238070,1238073,1239024-1239048,1239050-1239062,1239135,1239256,1239258-1239485,1239785-1240046,1240101,1240106,1240109,1240112
 ,1240114,1240116,1240118,1240121,1240329,1240474-1240850,1240857,1241087,1241160,1241408-1241822,1241908-1241909,1241912-1242110,1242371-1292130,1292334,1292338,1292345-1292347,1293155,1293831-1293832,1295998,1296284,1297014-1297015,1297017,1297158,1297177,1297202,1297209,1297213,1297717,1297722,1297729,1297768,1297778,1297818,1297828,1297979,1297987,1298121,1298140,1298590,1298592,1298628-1298629,1298794,1298983-1298984,1299020,1299034,1299819,1300154-1300155,1300569,1300948

Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/util/Conversions.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/util/Conversions.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/util/Conversions.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/util/Conversions.java Thu Mar 15 23:11:47 2012
@@ -32,7 +32,7 @@ public class Conversions {
 
         int shift = 0;
         long result = 0;
-        for (int i = input.length; i < 0; i--) {
+        for (int i = input.length - 1; i >= 0; i--) {
             result = result + ((input[i] & 0xFF) << shift);
             shift += 8;
         }

Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java Thu Mar 15 23:11:47 2012
@@ -21,4 +21,12 @@ package org.apache.catalina.websocket;
  */
 public class Constants {
     public static final String Package = "org.apache.catalina.websocket";
+
+    // OP Codes
+    public static final byte OPCODE_CONTINUATION = 0x00;
+    public static final byte OPCODE_TEXT = 0x01;
+    public static final byte OPCODE_BINARY = 0x02;
+    public static final byte OPCODE_CLOSE = 0x08;
+    public static final byte OPCODE_PING = 0x09;
+    public static final byte OPCODE_PONG = 0x0A;
 }

Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/MessageInbound.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/MessageInbound.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/MessageInbound.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/MessageInbound.java Thu Mar 15 23:11:47 2012
@@ -24,8 +24,10 @@ import java.nio.CharBuffer;
 
 public abstract class MessageInbound extends StreamInbound {
 
-    // TODO: Make buffer sizes configurable
-    // TODO: Allow buffers to expand
+    // 2MB - like maxPostSize
+    private int byteBufferMaxSize = 2097152;
+    private int charBufferMaxSize = 2097152;
+
     ByteBuffer bb = ByteBuffer.allocate(8192);
     CharBuffer cb = CharBuffer.allocate(8192);
 
@@ -34,6 +36,9 @@ public abstract class MessageInbound ext
         int read = 0;
         while (read > -1) {
             bb.position(bb.position() + read);
+            if (bb.remaining() == 0) {
+                resizeByteBuffer();
+            }
             read = is.read(bb.array(), bb.position(), bb.remaining());
         }
         bb.flip();
@@ -46,14 +51,70 @@ public abstract class MessageInbound ext
         int read = 0;
         while (read > -1) {
             cb.position(cb.position() + read);
+            if (cb.remaining() == 0) {
+                resizeCharBuffer();
+            }
             read = r.read(cb.array(), cb.position(), cb.remaining());
         }
-        cb.limit(cb.position());
-        cb.position(0);
+        cb.flip();
         onTextMessage(cb);
         cb.clear();
     }
 
+    private void resizeByteBuffer() throws IOException {
+        int maxSize = getByteBufferMaxSize();
+        if (bb.limit() >= maxSize) {
+            // TODO i18n
+            throw new IOException("Buffer not big enough for message");
+        }
+
+        long newSize = bb.limit() * 2;
+        if (newSize > maxSize) {
+            newSize = maxSize;
+        }
+
+        // Cast is safe. newSize < maxSize and maxSize is an int
+        ByteBuffer newBuffer = ByteBuffer.allocate((int) newSize);
+        bb.rewind();
+        newBuffer.put(bb);
+        bb = newBuffer;
+    }
+
+    private void resizeCharBuffer() throws IOException {
+        int maxSize = getCharBufferMaxSize();
+        if (cb.limit() >= maxSize) {
+            // TODO i18n
+            throw new IOException("Buffer not big enough for message");
+        }
+
+        long newSize = cb.limit() * 2;
+        if (newSize > maxSize) {
+            newSize = maxSize;
+        }
+
+        // Cast is safe. newSize < maxSize and maxSize is an int
+        CharBuffer newBuffer = CharBuffer.allocate((int) newSize);
+        cb.rewind();
+        newBuffer.put(cb);
+        cb = newBuffer;
+    }
+
+    public int getByteBufferMaxSize() {
+        return byteBufferMaxSize;
+    }
+
+    public void setByteBufferMaxSize(int byteBufferMaxSize) {
+        this.byteBufferMaxSize = byteBufferMaxSize;
+    }
+
+    public int getCharBufferMaxSize() {
+        return charBufferMaxSize;
+    }
+
+    public void setCharBufferMaxSize(int charBufferMaxSize) {
+        this.charBufferMaxSize = charBufferMaxSize;
+    }
+
     protected abstract void onBinaryMessage(ByteBuffer message)
             throws IOException;
     protected abstract void onTextMessage(CharBuffer message)

Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java Thu Mar 15 23:11:47 2012
@@ -20,8 +20,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.nio.ByteBuffer;
 
-import org.apache.catalina.util.Conversions;
 import org.apache.coyote.http11.upgrade.UpgradeInbound;
 import org.apache.coyote.http11.upgrade.UpgradeOutbound;
 import org.apache.coyote.http11.upgrade.UpgradeProcessor;
@@ -30,18 +30,6 @@ import org.apache.tomcat.util.net.Abstra
 
 public abstract class StreamInbound implements UpgradeInbound {
 
-    // These attributes apply to the current frame being processed
-    private boolean fin = true;
-    private boolean rsv1 = false;
-    private boolean rsv2 = false;
-    private boolean rsv3 = false;
-    private int opCode = -1;
-    private long payloadLength = -1;
-
-    // These attributes apply to the message that may be spread over multiple
-    // frames
-    // TODO
-
     private UpgradeProcessor<?> processor = null;
     private WsOutbound outbound;
 
@@ -56,93 +44,102 @@ public abstract class StreamInbound impl
         this.processor = processor;
     }
 
-    public WsOutbound getStreamOutbound() {
+    public WsOutbound getOutbound() {
         return outbound;
     }
 
     @Override
     public SocketState onData() throws IOException {
-        // Must be start the start of a frame
-
-        // Read the first byte
-        int i = processor.read();
+        // Must be start the start of a frame or series of frames
+        WsInputStream wsIs = new WsInputStream(processor);
 
-        fin = (i & 0x80) > 0;
+        WsFrameHeader header = wsIs.getFrameHeader();
 
-        rsv1 = (i & 0x40) > 0;
-        rsv2 = (i & 0x20) > 0;
-        rsv3 = (i & 0x10) > 0;
-
-        if (rsv1 || rsv2 || rsv3) {
-            // TODO: Not supported.
+        // TODO User defined extensions may define values for rsv
+        if (header.getRsv() > 0) {
+            getOutbound().close(1002, null);
+            return SocketState.CLOSED;
         }
 
-        opCode = (i & 0x0F);
-        validateOpCode(opCode);
+        byte opCode = header.getOpCode();
 
-        // Read the next byte
-        i = processor.read();
+        if (opCode == Constants.OPCODE_BINARY) {
+            onBinaryData(wsIs);
+            return SocketState.UPGRADED;
+        } else if (opCode == Constants.OPCODE_TEXT) {
+            InputStreamReader r =
+                    new InputStreamReader(wsIs, B2CConverter.UTF_8);
+            onTextData(r);
+            return SocketState.UPGRADED;
+        }
 
-        // Client data must be masked and this isn't
-        if ((i & 0x80) == 0) {
-            // TODO: Better message
-            throw new IOException();
+        // Must be a control frame and control frames:
+        // - have a limited payload length
+        // - must not be fragmented
+        if (wsIs.getPayloadLength() > 125 || !wsIs.getFrameHeader().getFin()) {
+            getOutbound().close(1002, null);
+            return SocketState.CLOSED;
         }
 
-        payloadLength = i & 0x7F;
-        if (payloadLength == 126) {
-            byte[] extended = new byte[2];
-            processor.read(extended);
-            payloadLength = Conversions.byteArrayToLong(extended);
-        } else if (payloadLength == 127) {
-            byte[] extended = new byte[8];
-            processor.read(extended);
-            payloadLength = Conversions.byteArrayToLong(extended);
+        if (opCode == Constants.OPCODE_CLOSE){
+            doClose(wsIs);
+            return SocketState.CLOSED;
+        } else if (opCode == Constants.OPCODE_PING) {
+            doPing(wsIs);
+            return SocketState.UPGRADED;
+        } else if (opCode == Constants.OPCODE_PONG) {
+            doPong(wsIs);
+            return SocketState.UPGRADED;
         }
 
-        byte[] mask = new byte[4];
-        processor.read(mask);
+        getOutbound().close(1002, null);
+        return SocketState.CLOSED;
+    }
 
-        if (opCode == 1 || opCode == 2) {
-            WsInputStream wsIs = new WsInputStream(processor, mask,
-                    payloadLength);
-            if (opCode == 2) {
-                onBinaryData(wsIs);
-            } else {
-                InputStreamReader r =
-                        new InputStreamReader(wsIs, B2CConverter.UTF_8);
-                onTextData(r);
+    private void doClose(InputStream is) throws IOException {
+        // Control messages have a max size of 125 bytes. Need to try and read
+        // one more so we reach end of stream (less 2 for the status)
+        ByteBuffer data = ByteBuffer.allocate(124);
+
+        int status = is.read();
+        if (status != -1) {
+            status = status << 8;
+            status = status + is.read();
+            int read = 0;
+            while (read > -1) {
+                data.position(data.position() + read);
+                read = is.read(data.array(), data.position(), data.remaining());
             }
+        } else {
+            status = 0;
         }
+        data.flip();
+        getOutbound().close(status, data);
+    }
 
-        // TODO: Doesn't currently handle multi-frame messages. That will need
-        //       some refactoring.
-
-        // TODO: Per frame extension handling is not currently supported.
-
-        // TODO: Handle other control frames.
+    private void doPing(InputStream is) throws IOException {
+        // Control messages have a max size of 125 bytes. Need to try and read
+        // one more so we reach end of stream
+        ByteBuffer data = ByteBuffer.allocate(126);
+
+        int read = 0;
+        while (read > -1) {
+            data.position(data.position() + read);
+            read = is.read(data.array(), data.position(), data.remaining());
+        }
 
-        // TODO: Handle control frames appearing in the middle of a multi-frame
-        //       message
+        data.flip();
+        getOutbound().pong(data);
+    }
 
-        return SocketState.UPGRADED;
+    private void doPong(InputStream is) throws IOException {
+        // Unsolicited pong - swallow it
+        int read = 0;
+        while (read > -1) {
+            read = is.read();
+        }
     }
 
     protected abstract void onBinaryData(InputStream is) throws IOException;
     protected abstract void onTextData(Reader r) throws IOException;
-
-    private void validateOpCode(int opCode) throws IOException {
-        switch (opCode) {
-        case 0:
-        case 1:
-        case 2:
-        case 8:
-        case 9:
-        case 10:
-            break;
-        default:
-            // TODO: Message
-            throw new IOException();
-        }
-    }
 }

Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java Thu Mar 15 23:11:47 2012
@@ -19,6 +19,7 @@ package org.apache.catalina.websocket;
 import java.io.IOException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
@@ -68,17 +69,17 @@ public abstract class WebSocketServlet e
         String subProtocol = null;
         List<String> extensions = Collections.emptyList();
 
-        if (!headerContains(req, "upgrade", "websocket")) {
+        if (!headerContainsToken(req, "upgrade", "websocket")) {
             resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
             return;
         }
 
-        if (!headerContains(req, "connection", "upgrade")) {
+        if (!headerContainsToken(req, "connection", "upgrade")) {
             resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
             return;
         }
 
-        if (!headerContains(req, "sec-websocket-version", "13")) {
+        if (!headerContainsToken(req, "sec-websocket-version", "13")) {
             resp.setStatus(426);
             resp.setHeader("Sec-WebSocket-Version", "13");
             return;
@@ -96,8 +97,14 @@ public abstract class WebSocketServlet e
             return;
         }
 
-        // TODO Read client handshake - Sec-WebSocket-Protocol
-        //                              Sec-WebSocket-Extensions
+        List<String> subProtocols = getTokensFromHeader(req,
+                "Sec-WebSocket-Protocol-Client");
+        if (!subProtocols.isEmpty()) {
+            subProtocol = selectSubProtocol(subProtocols);
+
+        }
+
+        // TODO Read client handshake - Sec-WebSocket-Extensions
 
         // TODO Extensions require the ability to specify something (API TBD)
         //      that can be passed to the Tomcat internals and process extension
@@ -108,27 +115,27 @@ public abstract class WebSocketServlet e
         resp.setHeader("connection", "upgrade");
         resp.setHeader("Sec-WebSocket-Accept", getWebSocketAccept(key));
         if (subProtocol != null) {
-            // TODO
+            resp.setHeader("Sec-WebSocket-Protocol", subProtocol);
         }
         if (!extensions.isEmpty()) {
             // TODO
         }
 
         // Small hack until the Servlet API provides a way to do this.
-        StreamInbound inbound = createWebSocketInbound();
+        StreamInbound inbound = createWebSocketInbound(subProtocol);
         ((RequestFacade) req).doUpgrade(inbound);
     }
 
 
-    private boolean headerContains(HttpServletRequest req, String headerName,
-            String target) {
+    /*
+     * This only works for tokens. Quoted strings need more sophisticated
+     * parsing.
+     */
+    private boolean headerContainsToken(HttpServletRequest req,
+            String headerName, String target) {
         Enumeration<String> headers = req.getHeaders(headerName);
         while (headers.hasMoreElements()) {
             String header = headers.nextElement();
-            // TODO Splitting headers into tokens isn't quite this simple but
-            //      this should be OK in this case. It is tempting to change the
-            //      header parsing code so there is a one to one mapping between
-            //      token and enumeration entry.
             String[] tokens = header.split(",");
             for (String token : tokens) {
                 if (target.equalsIgnoreCase(token.trim())) {
@@ -140,6 +147,26 @@ public abstract class WebSocketServlet e
     }
 
 
+    /*
+     * This only works for tokens. Quoted strings need more sophisticated
+     * parsing.
+     */
+    private List<String> getTokensFromHeader(HttpServletRequest req,
+            String headerName) {
+        List<String> result = new ArrayList<String>();
+
+        Enumeration<String> headers = req.getHeaders(headerName);
+        while (headers.hasMoreElements()) {
+            String header = headers.nextElement();
+            String[] tokens = header.split(",");
+            for (String token : tokens) {
+                result.add(token.trim());
+            }
+        }
+        return result;
+    }
+
+
     private String getWebSocketAccept(String key) {
         synchronized (sha1Helper) {
             sha1Helper.reset();
@@ -163,5 +190,27 @@ public abstract class WebSocketServlet e
         return true;
     }
 
-    protected abstract StreamInbound createWebSocketInbound();
+    /**
+     * Intended to be overridden by sub-classes that wish to select a
+     * sub-protocol if the client provides a list of supported protocols.
+     *
+     * @param subProtocols  The list of sub-protocols supported by the client
+     *                      in client preference order. The server is under no
+     *                      obligation to respect the declared preference
+     * @return  <code>null</code> if no sub-protocol is selected or the name of
+     *          the protocol which <b>must</b> be one of the protocols listed by
+     *          the client.
+     */
+    protected String selectSubProtocol(List<String> subProtocols) {
+        return null;
+    }
+
+    /**
+     * Create the instance that will process this inbound connection.
+     *
+     * @param subProtocol   The sub-protocol agreed between the client and
+     *                      server or <code>null</code> if none was agreed
+     * @return
+     */
+    protected abstract StreamInbound createWebSocketInbound(String subProtocol);
 }

Copied: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java (from r1245848, tomcat/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java)
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java?p2=tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java&p1=tomcat/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java&r1=1245848&r2=1301263&rev=1301263&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrameHeader.java Thu Mar 15 23:11:47 2012
@@ -23,17 +23,13 @@ package org.apache.catalina.websocket;
 public class WsFrameHeader {
 
     private final boolean fin;
-    private final boolean rsv1;
-    private final boolean rsv2;
-    private final boolean rsv3;
+    private final int rsv;
     private final byte opCode;
 
     public WsFrameHeader(int b) {
         fin = (b & 0x80) > 0;
 
-        rsv1 = (b & 0x40) > 0;
-        rsv2 = (b & 0x20) > 0;
-        rsv3 = (b & 0x10) > 0;
+        rsv = (b & 0x70) >>> 4;
 
         opCode = (byte) (b & 0x0F);
     }
@@ -42,21 +38,11 @@ public class WsFrameHeader {
         return fin;
     }
 
-    public boolean getRsv1() {
-        return rsv1;
-    }
-
-    public boolean getRsv2() {
-        return rsv2;
-    }
-
-    public boolean getRsv3() {
-        return rsv3;
+    public int getRsv() {
+        return rsv;
     }
 
     public byte getOpCode() {
         return opCode;
     }
-
-
 }

Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java Thu Mar 15 23:11:47 2012
@@ -18,34 +18,96 @@ package org.apache.catalina.websocket;
 
 import java.io.IOException;
 
+import org.apache.catalina.util.Conversions;
 import org.apache.coyote.http11.upgrade.UpgradeProcessor;
 
 public class WsInputStream extends java.io.InputStream {
 
     private UpgradeProcessor<?> processor;
-    private byte[] mask;
+    private WsFrameHeader wsFrameHeader;
+    private long payloadLength = -1;
+    private int[] mask = new int[4];
+
+
     private long remaining;
-    private long read;
+    private long readThisFragment;
 
-    public WsInputStream(UpgradeProcessor<?> processor, byte[] mask,
-            long remaining) {
+    public WsInputStream(UpgradeProcessor<?> processor) throws IOException {
         this.processor = processor;
-        this.mask = mask;
-        this.remaining = remaining;
-        this.read = 0;
+
+        processFrameHeader();
     }
 
+
+    private void processFrameHeader() throws IOException {
+
+        // TODO: Per frame extension handling is not currently supported.
+
+        // TODO: Handle control frames between fragments
+
+        int i = processor.read();
+        this.wsFrameHeader = new WsFrameHeader(i);
+
+        // Client data must be masked
+        i = processor.read();
+        if ((i & 0x80) == 0) {
+            // TODO: StringManager / i18n
+            throw new IOException("Client frame not masked");
+        }
+
+        payloadLength = i & 0x7F;
+        if (payloadLength == 126) {
+            byte[] extended = new byte[2];
+            processor.read(extended);
+            payloadLength = Conversions.byteArrayToLong(extended);
+        } else if (payloadLength == 127) {
+            byte[] extended = new byte[8];
+            processor.read(extended);
+            payloadLength = Conversions.byteArrayToLong(extended);
+        }
+        remaining = payloadLength;
+
+        for (int j = 0; j < mask.length; j++) {
+            mask[j] = processor.read() & 0xFF;
+        }
+
+        readThisFragment = 0;
+    }
+
+    public WsFrameHeader getFrameHeader() {
+        return wsFrameHeader;
+    }
+
+    public long getPayloadLength() {
+        return payloadLength;
+    }
+
+
+    // ----------------------------------------------------- InputStream methods
+
     @Override
     public int read() throws IOException {
+        while (remaining == 0 && !getFrameHeader().getFin()) {
+            // Need more data - process next frame
+            processFrameHeader();
+
+            if (getFrameHeader().getOpCode() != Constants.OPCODE_CONTINUATION) {
+                // TODO i18n
+                throw new IOException("Not a continuation frame");
+            }
+        }
+
         if (remaining == 0) {
             return -1;
         }
 
         remaining--;
-        read++;
+        readThisFragment++;
 
         int masked = processor.read();
-        return masked ^ mask[(int) ((read - 1) % 4)];
+        if(masked == -1) {
+            return -1;
+        }
+        return masked ^ mask[(int) ((readThisFragment - 1) % 4)];
     }
-
 }

Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java Thu Mar 15 23:11:47 2012
@@ -25,11 +25,12 @@ import org.apache.tomcat.util.buf.B2CCon
 
 public class WsOutbound {
 
-    private static final int DEFAULT_BUFFER_SIZE = 2048;
+    private static final int DEFAULT_BUFFER_SIZE = 8192;
 
     private UpgradeOutbound upgradeOutbound;
     private ByteBuffer bb;
     private CharBuffer cb;
+    private boolean closed = false;
     protected Boolean text = null;
     protected boolean firstFrame = true;
 
@@ -37,9 +38,7 @@ public class WsOutbound {
     public WsOutbound(UpgradeOutbound upgradeOutbound) {
         this.upgradeOutbound = upgradeOutbound;
         // TODO: Make buffer size configurable
-        // Byte buffer needs to be 4* char buffer to be sure that char buffer
-        // can always we written into Byte buffer
-        this.bb = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE * 4);
+        this.bb = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
         this.cb = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
     }
 
@@ -81,7 +80,7 @@ public class WsOutbound {
             flush();
         }
         text = Boolean.FALSE;
-        doWriteBinary(msgBb, true);
+        doWriteBytes(msgBb, true);
     }
 
 
@@ -105,28 +104,71 @@ public class WsOutbound {
             return;
         }
         if (text.booleanValue()) {
+            cb.flip();
             doWriteText(cb, finalFragment);
         } else {
-            doWriteBinary(bb, finalFragment);
+            bb.flip();
+            doWriteBytes(bb, finalFragment);
         }
     }
 
 
-    public void close() throws IOException {
+    public void close(int status, ByteBuffer data) throws IOException {
+        // TODO Think about threading requirements for writing. This is not
+        // currently thread safe and writing almost certainly needs to be.
+        if (closed) {
+            return;
+        }
+        closed = true;
+
         doFlush(true);
 
-        // TODO: Send a close message
+        upgradeOutbound.write(0x88);
+        if (status == 0) {
+            upgradeOutbound.write(0);
+        } else if (data == null) {
+            upgradeOutbound.write(2);
+            upgradeOutbound.write(status >>> 8);
+            upgradeOutbound.write(status);
+        } else {
+            upgradeOutbound.write(2 + data.limit());
+            upgradeOutbound.write(status >>> 8);
+            upgradeOutbound.write(status);
+            upgradeOutbound.write(data.array(), 0, data.limit());
+        }
+        upgradeOutbound.flush();
+
         bb = null;
         cb = null;
         upgradeOutbound = null;
     }
 
+    public void pong(ByteBuffer data) throws IOException {
+        // TODO Think about threading requirements for writing. This is not
+        // currently thread safe and writing almost certainly needs to be.
+        if (closed) {
+            // TODO - handle this - ISE?
+        }
 
-    protected void doWriteBinary(ByteBuffer buffer, boolean finalFragment)
-            throws IOException {
+        doFlush(true);
+
+        upgradeOutbound.write(0x8A);
+        upgradeOutbound.write(data.limit());
+        upgradeOutbound.write(data.array(), 0, data.limit());
+
+        upgradeOutbound.flush();
+    }
 
-        // Prepare to write
-        buffer.flip();
+    /**
+     * Writes the provided bytes as the payload in a new WebSocket frame.
+     *
+     * @param buffer        The bytes to include in the payload.
+     * @param finalFragment Do these bytes represent the final fragment of a
+     *                      WebSocket message?
+     * @throws IOException
+     */
+    private void doWriteBytes(ByteBuffer buffer, boolean finalFragment)
+            throws IOException {
 
         // Work out the first byte
         int first = 0x00;
@@ -143,13 +185,24 @@ public class WsOutbound {
         // Continuation frame is OpCode 0
         upgradeOutbound.write(first);
 
-        // Note: buffer will never be more than 2^16 in length
         if (buffer.limit() < 126) {
             upgradeOutbound.write(buffer.limit());
-        } else {
+        } else if (buffer.limit() < 65536) {
             upgradeOutbound.write(126);
             upgradeOutbound.write(buffer.limit() >>> 8);
             upgradeOutbound.write(buffer.limit() & 0xFF);
+        } else {
+            // Will never be more than 2^31-1
+            upgradeOutbound.write(127);
+            upgradeOutbound.write(0);
+            upgradeOutbound.write(0);
+            upgradeOutbound.write(0);
+            upgradeOutbound.write(0);
+            upgradeOutbound.write(buffer.limit() >>> 24);
+            upgradeOutbound.write(buffer.limit() >>> 16);
+            upgradeOutbound.write(buffer.limit() >>> 8);
+            upgradeOutbound.write(buffer.limit() & 0xFF);
+
         }
 
         // Write the content
@@ -167,12 +220,19 @@ public class WsOutbound {
     }
 
 
-    protected void doWriteText(CharBuffer buffer, boolean finalFragment)
+    private void doWriteText(CharBuffer buffer, boolean finalFragment)
             throws IOException {
-        buffer.flip();
-        B2CConverter.UTF_8.newEncoder().encode(buffer, bb, true);
-        doWriteBinary(bb, finalFragment);
-        // Reset
+        do {
+            B2CConverter.UTF_8.newEncoder().encode(buffer, bb, true);
+            bb.flip();
+            if (buffer.hasRemaining()) {
+                doWriteBytes(bb, false);
+            } else {
+                doWriteBytes(bb, finalFragment);
+            }
+        } while (buffer.hasRemaining());
+
+        // Reset - bb will be cleared in doWriteBytes()
         cb.clear();
     }
 }

Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java Thu Mar 15 23:11:47 2012
@@ -30,6 +30,7 @@ import javax.management.MalformedObjectN
 import javax.management.ObjectName;
 
 import org.apache.coyote.http11.upgrade.UpgradeInbound;
+import org.apache.coyote.http11.upgrade.UpgradeProcessor;
 import org.apache.juli.logging.Log;
 import org.apache.tomcat.util.ExceptionUtils;
 import org.apache.tomcat.util.modeler.Registry;
@@ -592,7 +593,9 @@ public abstract class AbstractProtocol i
                     upgradePoll(socket, processor);
                 } else {
                     // Connection closed. OK to recycle the processor.
-                    release(socket, processor, true, false);
+                    if (!(processor instanceof UpgradeProcessor)) {
+                        release(socket, processor, true, false);
+                    }
                 }
                 return state;
             } catch(java.net.SocketException e) {
@@ -614,7 +617,10 @@ public abstract class AbstractProtocol i
                 // less-than-verbose logs.
                 getLog().error(sm.getString("ajpprotocol.proto.error"), e);
             }
-            release(socket, processor, true, false);
+            // Don't try to add upgrade processors back into the pool
+            if (!(processor instanceof UpgradeProcessor)) {
+                release(socket, processor, true, false);
+            }
             return SocketState.CLOSED;
         }
         

Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/Constants.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/Constants.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/Constants.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/Constants.java Thu Mar 15 23:11:47 2012
@@ -1,22 +1,22 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-public class Constants {
-
-    public static final String Package = "org.apache.coyote.http11.upgrade";
-}
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.coyote.http11.upgrade;
+
+public class Constants {
+
+    public static final String Package = "org.apache.coyote.http11.upgrade";
+}

Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java Thu Mar 15 23:11:47 2012
@@ -1,73 +1,73 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-
-import org.apache.tomcat.jni.Socket;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Implementation note: The need to extend Http11Processor could probably be
- * removed if the Processor interface was expanded to cover all of the methods
- * required by the AbstractProtocol. That would simplify the code and further
- * reduce the size of instances of this class.
- */
-public class UpgradeAprProcessor extends UpgradeProcessor<Long> {
-
-    long socket;
-
-
-    public UpgradeAprProcessor(SocketWrapper<Long> wrapper,
-            UpgradeInbound upgradeInbound) {
-        super(upgradeInbound);
-
-        this.socket = wrapper.getSocket().longValue();
-    }
-
-
-    /*
-     * Output methods
-     */
-    @Override
-    public void flush() throws IOException {
-        // NOOP
-    }
-
-
-    @Override
-    public void write(int b) throws IOException {
-        Socket.send(socket, new byte[] {(byte) b}, 0, 1);
-    }
-
-
-    /*
-     * Input methods
-     */
-    @Override
-    public int read() throws IOException {
-        byte[] bytes = new byte[1];
-        Socket.recv(socket, bytes, 0, 1);
-        return bytes[0];
-    }
-
-
-    @Override
-    public int read(byte[] bytes) throws IOException {
-        return Socket.recv(socket, bytes, 0, bytes.length);
-    }
-}
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.coyote.http11.upgrade;
+
+import java.io.IOException;
+
+import org.apache.tomcat.jni.Socket;
+import org.apache.tomcat.util.net.SocketWrapper;
+
+/**
+ * Implementation note: The need to extend Http11Processor could probably be
+ * removed if the Processor interface was expanded to cover all of the methods
+ * required by the AbstractProtocol. That would simplify the code and further
+ * reduce the size of instances of this class.
+ */
+public class UpgradeAprProcessor extends UpgradeProcessor<Long> {
+
+    private final long socket;
+
+
+    public UpgradeAprProcessor(SocketWrapper<Long> wrapper,
+            UpgradeInbound upgradeInbound) {
+        super(upgradeInbound);
+
+        this.socket = wrapper.getSocket().longValue();
+    }
+
+
+    /*
+     * Output methods
+     */
+    @Override
+    public void flush() throws IOException {
+        // NOOP
+    }
+
+
+    @Override
+    public void write(int b) throws IOException {
+        Socket.send(socket, new byte[] {(byte) b}, 0, 1);
+    }
+
+
+    /*
+     * Input methods
+     */
+    @Override
+    public int read() throws IOException {
+        byte[] bytes = new byte[1];
+        Socket.recv(socket, bytes, 0, 1);
+        return bytes[0];
+    }
+
+
+    @Override
+    public int read(byte[] bytes) throws IOException {
+        return Socket.recv(socket, bytes, 0, bytes.length);
+    }
+}

Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java Thu Mar 15 23:11:47 2012
@@ -1,74 +1,74 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Implementation note: The need to extend Http11Processor could probably be
- * removed if the Processor interface was expanded to cover all of the methods
- * required by the AbstractProtocol. That would simplify the code and further
- * reduce the size of instances of this class.
- */
-public class UpgradeBioProcessor extends UpgradeProcessor<Socket> {
-
-    private InputStream inputStream;
-    private OutputStream outputStream;
-
-    public UpgradeBioProcessor(SocketWrapper<Socket> wrapper,
-            UpgradeInbound upgradeInbound) throws IOException {
-        super(upgradeInbound);
-
-        this.inputStream = wrapper.getSocket().getInputStream();
-        this.outputStream = wrapper.getSocket().getOutputStream();
-    }
-
-
-    /*
-     * Output methods
-     */
-    @Override
-    public void flush() throws IOException {
-        outputStream.flush();
-    }
-
-
-    @Override
-    public void write(int b) throws IOException {
-        outputStream.write(b);
-    }
-
-
-    /*
-     * Input methods
-     */
-    @Override
-    public int read() throws IOException {
-        return inputStream.read();
-    }
-
-
-    @Override
-    public int read(byte[] bytes) throws IOException {
-        return inputStream.read(bytes);
-    }
-}
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.coyote.http11.upgrade;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import org.apache.tomcat.util.net.SocketWrapper;
+
+/**
+ * Implementation note: The need to extend Http11Processor could probably be
+ * removed if the Processor interface was expanded to cover all of the methods
+ * required by the AbstractProtocol. That would simplify the code and further
+ * reduce the size of instances of this class.
+ */
+public class UpgradeBioProcessor extends UpgradeProcessor<Socket> {
+
+    private final InputStream inputStream;
+    private final OutputStream outputStream;
+
+    public UpgradeBioProcessor(SocketWrapper<Socket> wrapper,
+            UpgradeInbound upgradeInbound) throws IOException {
+        super(upgradeInbound);
+
+        this.inputStream = wrapper.getSocket().getInputStream();
+        this.outputStream = wrapper.getSocket().getOutputStream();
+    }
+
+
+    /*
+     * Output methods
+     */
+    @Override
+    public void flush() throws IOException {
+        outputStream.flush();
+    }
+
+
+    @Override
+    public void write(int b) throws IOException {
+        outputStream.write(b);
+    }
+
+
+    /*
+     * Input methods
+     */
+    @Override
+    public int read() throws IOException {
+        return inputStream.read();
+    }
+
+
+    @Override
+    public int read(byte[] bytes) throws IOException {
+        return inputStream.read(bytes);
+    }
+}

Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java Thu Mar 15 23:11:47 2012
@@ -33,8 +33,8 @@ import org.apache.tomcat.util.net.Socket
  */
 public class UpgradeNioProcessor extends UpgradeProcessor<NioChannel> {
 
-    private NioChannel nioChannel;
-    private NioSelectorPool pool;
+    private final NioChannel nioChannel;
+    private final NioSelectorPool pool;
 
     public UpgradeNioProcessor(SocketWrapper<NioChannel> wrapper,
             UpgradeInbound upgradeInbound, NioSelectorPool pool) {

Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java Thu Mar 15 23:11:47 2012
@@ -32,7 +32,7 @@ public class UpgradeOutbound extends Out
         processor.flush();
     }
 
-    private UpgradeProcessor<?> processor;
+    private final UpgradeProcessor<?> processor;
 
     public UpgradeOutbound(UpgradeProcessor<?> processor) {
         this.processor = processor;

Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java Thu Mar 15 23:11:47 2012
@@ -1,118 +1,117 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-import java.util.concurrent.Executor;
-
-import org.apache.coyote.Processor;
-import org.apache.coyote.Request;
-import org.apache.coyote.http11.Constants;
-import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-import org.apache.tomcat.util.res.StringManager;
-
-public abstract class UpgradeProcessor<S> implements Processor<S> {
-
-    protected static final StringManager sm =
-            StringManager.getManager(Constants.Package);
-
-    private final UpgradeInbound upgradeInbound;
-
-    protected UpgradeProcessor (UpgradeInbound upgradeInbound) {
-        this.upgradeInbound = upgradeInbound;
-        upgradeInbound.setUpgradeProcessor(this);
-        upgradeInbound.setUpgradeOutbound(new UpgradeOutbound(this));
-    }
-
-    // Output methods
-    public abstract void flush() throws IOException;
-    public abstract void write(int b) throws IOException;
-
-    // Input methods
-    public abstract int read() throws IOException;
-    public abstract int read(byte[] bytes) throws IOException;
-
-    @Override
-    public final UpgradeInbound getUpgradeInbound() {
-        return upgradeInbound;
-    }
-
-    @Override
-    public final SocketState upgradeDispatch() throws IOException {
-        return upgradeInbound.onData();
-    }
-
-    @Override
-    public final boolean isUpgrade() {
-        return true;
-    }
-
-    @Override
-    public final void recycle(boolean socketClosing) {
-        // Currently a NO-OP as upgrade processors are not recycled.
-    }
-
-    // NO-OP methods for upgrade
-    @Override
-    public final Executor getExecutor() {
-        return null;
-    }
-
-    @Override
-    public final SocketState process(SocketWrapper<S> socketWrapper)
-            throws IOException {
-        return null;
-    }
-
-    @Override
-    public final SocketState event(SocketStatus status) throws IOException {
-        return null;
-    }
-
-    @Override
-    public final SocketState asyncDispatch(SocketStatus status) {
-        return null;
-    }
-
-    @Override
-    public final SocketState asyncPostProcess() {
-        return null;
-    }
-
-    @Override
-    public final boolean isComet() {
-        return false;
-    }
-
-    @Override
-    public final boolean isAsync() {
-        return false;
-    }
-
-    @Override
-    public final Request getRequest() {
-        return null;
-    }
-
-    @Override
-    public final void setSslSupport(SSLSupport sslSupport) {
-        // NOOP
-    }
-}
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.coyote.http11.upgrade;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+
+import org.apache.coyote.Processor;
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.SocketWrapper;
+import org.apache.tomcat.util.res.StringManager;
+
+public abstract class UpgradeProcessor<S> implements Processor<S> {
+
+    protected static final StringManager sm =
+            StringManager.getManager(Constants.Package);
+
+    private final UpgradeInbound upgradeInbound;
+
+    protected UpgradeProcessor (UpgradeInbound upgradeInbound) {
+        this.upgradeInbound = upgradeInbound;
+        upgradeInbound.setUpgradeProcessor(this);
+        upgradeInbound.setUpgradeOutbound(new UpgradeOutbound(this));
+    }
+
+    // Output methods
+    public abstract void flush() throws IOException;
+    public abstract void write(int b) throws IOException;
+
+    // Input methods
+    public abstract int read() throws IOException;
+    public abstract int read(byte[] bytes) throws IOException;
+
+    @Override
+    public final UpgradeInbound getUpgradeInbound() {
+        return upgradeInbound;
+    }
+
+    @Override
+    public final SocketState upgradeDispatch() throws IOException {
+        return upgradeInbound.onData();
+    }
+
+    @Override
+    public final boolean isUpgrade() {
+        return true;
+    }
+
+    @Override
+    public final void recycle(boolean socketClosing) {
+        // Currently a NO-OP as upgrade processors are not recycled.
+    }
+
+    // NO-OP methods for upgrade
+    @Override
+    public final Executor getExecutor() {
+        return null;
+    }
+
+    @Override
+    public final SocketState process(SocketWrapper<S> socketWrapper)
+            throws IOException {
+        return null;
+    }
+
+    @Override
+    public final SocketState event(SocketStatus status) throws IOException {
+        return null;
+    }
+
+    @Override
+    public final SocketState asyncDispatch(SocketStatus status) {
+        return null;
+    }
+
+    @Override
+    public final SocketState asyncPostProcess() {
+        return null;
+    }
+
+    @Override
+    public final boolean isComet() {
+        return false;
+    }
+
+    @Override
+    public final boolean isAsync() {
+        return false;
+    }
+
+    @Override
+    public final Request getRequest() {
+        return null;
+    }
+
+    @Override
+    public final void setSslSupport(SSLSupport sslSupport) {
+        // NOOP
+    }
+}

Modified: tomcat/tc7.0.x/trunk/test/org/apache/catalina/websocket/TestWebSocket.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/test/org/apache/catalina/websocket/TestWebSocket.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/test/org/apache/catalina/websocket/TestWebSocket.java (original)
+++ tomcat/tc7.0.x/trunk/test/org/apache/catalina/websocket/TestWebSocket.java Thu Mar 15 23:11:47 2012
@@ -1,83 +1,164 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.catalina.websocket;
-
-import java.io.InputStream;
-import java.io.Reader;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-
-import org.junit.Test;
-
-import org.apache.catalina.startup.TomcatBaseTest;
-
-public class TestWebSocket extends TomcatBaseTest {
-
-    @Test
-    public void testSimple() {
-        // TODO: Write a test
-    }
-
-    private static final class StreamingWebSocketServlet
-            extends WebSocketServlet {
-
-        private static final long serialVersionUID = 1L;
-
-        @Override
-        protected StreamInbound createWebSocketInbound() {
-            return new SimpleStreamInbound();
-        }
-    }
-
-    private static final class SimpleStreamInbound extends StreamInbound {
-
-        @Override
-        protected void onBinaryData(InputStream is) {
-            // TODO Auto-generated method stub
-        }
-
-        @Override
-        protected void onTextData(Reader r) {
-            // TODO Auto-generated method stub
-        }
-    }
-
-
-    private static final class MessageWebSocketServlet
-            extends WebSocketServlet {
-
-        private static final long serialVersionUID = 1L;
-
-        @Override
-        protected StreamInbound createWebSocketInbound() {
-            return new SimpleMessageInbound();
-        }
-    }
-
-    private static final class SimpleMessageInbound extends MessageInbound {
-
-        @Override
-        protected void onBinaryMessage(ByteBuffer message) {
-            // TODO Auto-generated method stub
-        }
-
-        @Override
-        protected void onTextMessage(CharBuffer message) {
-            // TODO Auto-generated method stub
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.websocket;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.buf.CharChunk;
+
+public class TestWebSocket extends TomcatBaseTest {
+
+    private static final String CRLF = "\r\n";
+
+    private OutputStream os;
+    private InputStream is;
+    boolean isContinuation = false;
+
+    @Test
+    public void testSimple() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+        File appDir = new File(getBuildDirectory(), "webapps/examples");
+        tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
+
+        tomcat.start();
+
+        // Open the socket
+        final String encoding = "ISO-8859-1";
+        SocketAddress addr = new InetSocketAddress("localhost", getPort());
+        Socket socket = new Socket();
+        socket.setSoTimeout(10000);
+        socket.connect(addr, 10000);
+        os = socket.getOutputStream();
+        Writer writer = new OutputStreamWriter(os, encoding);
+        is = socket.getInputStream();
+        Reader r = new InputStreamReader(is, encoding);
+        BufferedReader reader = new BufferedReader(r);
+
+        // Send the WebSocket handshake
+        writer.write("GET /examples/websocket/echoStream HTTP/1.1" + CRLF);
+        writer.write("Host: foo" + CRLF);
+        writer.write("Upgrade: websocket" + CRLF);
+        writer.write("Connection: upgrade" + CRLF);
+        writer.write("Sec-WebSocket-Version: 13" + CRLF);
+        writer.write("Sec-WebSocket-Key: TODO" + CRLF);
+        writer.write(CRLF);
+        writer.flush();
+
+        // Make sure we got an upgrade response
+        String responseLine = reader.readLine();
+        assertTrue(responseLine.startsWith("HTTP/1.1 101"));
+
+        // Swallow the headers
+        String responseHeaderLine = reader.readLine();
+        while (!responseHeaderLine.equals("")) {
+            responseHeaderLine = reader.readLine();
+        }
+
+        // Now we can do WebSocket
+        sendMessage("foo", false);
+        sendMessage("foo", true);
+
+        assertEquals("foofoo",readMessage());
+
+        // Finished with the socket
+        socket.close();
+    }
+
+    private void sendMessage(String message, boolean finalFragment)
+            throws IOException{
+        ByteChunk bc = new ByteChunk(8192);
+        C2BConverter c2b = new C2BConverter(bc, "UTF-8");
+        c2b.convert(message);
+        c2b.flushBuffer();
+
+        int len = bc.getLength();
+        assertTrue(len < 126);
+
+
+        byte first;
+        if (isContinuation) {
+            first = Constants.OPCODE_CONTINUATION;
+        } else {
+            first = Constants.OPCODE_TEXT;
+        }
+        if (finalFragment) {
+            first = (byte) (0x80 | first);
+        }
+        os.write(first);
+
+        os.write(0x80 | len);
+
+        // Zero mask
+        os.write(0);
+        os.write(0);
+        os.write(0);
+        os.write(0);
+
+        // Payload
+        os.write(bc.getBytes(), bc.getStart(), len);
+
+        os.flush();
+
+        // Will the next frame be a continuation frame
+        isContinuation = !finalFragment;
+    }
+
+    private String readMessage() throws IOException {
+        ByteChunk bc = new ByteChunk(125);
+        CharChunk cc = new CharChunk(125);
+
+        // Skip first byte
+        is.read();
+
+        // Get payload length
+        int len = is.read() & 0x7F;
+        assertTrue(len < 126);
+
+        // Read payload
+        int read = 0;
+        while (read < len) {
+            read = read + is.read(bc.getBytes(), read, len - read);
+        }
+
+        bc.setEnd(len);
+
+        B2CConverter b2c = new B2CConverter("UTF-8");
+        b2c.convert(bc, cc, len);
+
+        return cc.toString();
+    }
+}

Propchange: tomcat/tc7.0.x/trunk/test/org/apache/catalina/websocket/TestWebSocket.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java Thu Mar 15 23:11:47 2012
@@ -1,50 +1,49 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package websocket;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-
-import org.apache.catalina.websocket.MessageInbound;
-import org.apache.catalina.websocket.StreamInbound;
-import org.apache.catalina.websocket.WebSocketServlet;
-
-
-public class EchoMessage extends WebSocketServlet {
-
-    private static final long serialVersionUID = 1L;
-
-    @Override
-    protected StreamInbound createWebSocketInbound() {
-        return new EchoMessageInbound();
-    }
-
-    private static final class EchoMessageInbound extends MessageInbound {
-
-        @Override
-        protected void onBinaryMessage(ByteBuffer message) throws IOException {
-            System.out.write(message.array(), 0, message.limit());
-            System.out.print('\n');
-        }
-
-        @Override
-        protected void onTextMessage(CharBuffer message) throws IOException {
-            System.out.println(message);
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package websocket;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+import org.apache.catalina.websocket.MessageInbound;
+import org.apache.catalina.websocket.StreamInbound;
+import org.apache.catalina.websocket.WebSocketServlet;
+
+
+public class EchoMessage extends WebSocketServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    protected StreamInbound createWebSocketInbound(String subProtocol) {
+        return new EchoMessageInbound();
+    }
+
+    private static final class EchoMessageInbound extends MessageInbound {
+
+        @Override
+        protected void onBinaryMessage(ByteBuffer message) throws IOException {
+            getOutbound().writeBinaryMessage(message);
+        }
+
+        @Override
+        protected void onTextMessage(CharBuffer message) throws IOException {
+            getOutbound().writeTextMessage(message);
+        }
+    }
+}

Propchange: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java Thu Mar 15 23:11:47 2012
@@ -1,67 +1,67 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package websocket;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-
-import org.apache.catalina.websocket.StreamInbound;
-import org.apache.catalina.websocket.WebSocketServlet;
-import org.apache.catalina.websocket.WsOutbound;
-
-
-public class EchoStream extends WebSocketServlet {
-
-    private static final long serialVersionUID = 1L;
-
-    @Override
-    protected StreamInbound createWebSocketInbound() {
-        return new EchoStreamInbound();
-    }
-
-    private static final class EchoStreamInbound extends StreamInbound {
-
-        @Override
-        protected void onBinaryData(InputStream is) throws IOException {
-            // Simply echo the data to back to the client.
-            WsOutbound outbound = getStreamOutbound();
-
-            int i = is.read();
-            while (i != -1) {
-                outbound.writeBinaryData(i);
-                i = is.read();
-            }
-
-            outbound.flush();
-        }
-
-        @Override
-        protected void onTextData(Reader r) throws IOException {
-            // Simply echo the data to back to the client.
-            WsOutbound outbound = getStreamOutbound();
-
-            int c = r.read();
-            while (c != -1) {
-                outbound.writeTextData((char) c);
-                c = r.read();
-            }
-
-            outbound.flush();
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package websocket;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import org.apache.catalina.websocket.StreamInbound;
+import org.apache.catalina.websocket.WebSocketServlet;
+import org.apache.catalina.websocket.WsOutbound;
+
+
+public class EchoStream extends WebSocketServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    protected StreamInbound createWebSocketInbound(String subProtocol) {
+        return new EchoStreamInbound();
+    }
+
+    private static final class EchoStreamInbound extends StreamInbound {
+
+        @Override
+        protected void onBinaryData(InputStream is) throws IOException {
+            // Simply echo the data to back to the client.
+            WsOutbound outbound = getOutbound();
+
+            int i = is.read();
+            while (i != -1) {
+                outbound.writeBinaryData(i);
+                i = is.read();
+            }
+
+            outbound.flush();
+        }
+
+        @Override
+        protected void onTextData(Reader r) throws IOException {
+            // Simply echo the data to back to the client.
+            WsOutbound outbound = getOutbound();
+
+            int c = r.read();
+            while (c != -1) {
+                outbound.writeTextData((char) c);
+                c = r.read();
+            }
+
+            outbound.flush();
+        }
+    }
+}

Propchange: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/tc7.0.x/trunk/webapps/examples/websocket/index.html
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/websocket/index.html?rev=1301263&r1=1301262&r2=1301263&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/websocket/index.html (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/websocket/index.html Thu Mar 15 23:11:47 2012
@@ -1,28 +1,28 @@
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML><HEAD><TITLE>Apache Tomcat WebSocket Examples</TITLE>
-<META http-equiv=Content-Type content="text/html">
-</HEAD>
-<BODY>
-<P>
-<H3>Apache Tomcat WebSocket Examples</H3>
-<P></P>
-<ul>
-<li><a href="echo.html">Echo example</a></li>
-</ul>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML><HEAD><TITLE>Apache Tomcat WebSocket Examples</TITLE>
+<META http-equiv=Content-Type content="text/html">
+</HEAD>
+<BODY>
+<P>
+<H3>Apache Tomcat WebSocket Examples</H3>
+<P></P>
+<ul>
+<li><a href="echo.html">Echo example</a></li>
+</ul>
 </BODY></HTML>
\ No newline at end of file



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