tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ma...@apache.org
Subject svn commit: r1239034 - in /tomcat/trunk: java/org/apache/catalina/connector/ java/org/apache/catalina/util/ java/org/apache/catalina/websocket/ java/org/apache/coyote/ java/org/apache/coyote/ajp/ java/org/apache/coyote/http11/ java/org/apache/tomcat/ut...
Date Wed, 01 Feb 2012 10:07:40 GMT
Author: markt
Date: Wed Feb  1 10:07:39 2012
New Revision: 1239034

URL: http://svn.apache.org/viewvc?rev=1239034&view=rev
Log:
Initial web socket implementation with example. See code for TODOs.

Added:
    tomcat/trunk/java/org/apache/catalina/util/Conversions.java
    tomcat/trunk/java/org/apache/catalina/websocket/
    tomcat/trunk/java/org/apache/catalina/websocket/Constants.java
    tomcat/trunk/java/org/apache/catalina/websocket/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/websocket/MessageInbound.java
    tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java
    tomcat/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java
    tomcat/trunk/java/org/apache/catalina/websocket/WsInputStream.java
    tomcat/trunk/java/org/apache/catalina/websocket/WsOutbound.java
    tomcat/trunk/java/org/apache/coyote/http11/UpgradeInbound.java
    tomcat/trunk/java/org/apache/coyote/http11/UpgradeInputStream.java
    tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutbound.java
    tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutputStream.java
    tomcat/trunk/test/org/apache/catalina/websocket/
    tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java
    tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/
    tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java
    tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java
    tomcat/trunk/webapps/examples/websocket/
    tomcat/trunk/webapps/examples/websocket/echo.html
    tomcat/trunk/webapps/examples/websocket/index.html
      - copied, changed from r1239024, tomcat/trunk/webapps/examples/index.html
Modified:
    tomcat/trunk/java/org/apache/catalina/connector/Request.java
    tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java
    tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java
    tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java
    tomcat/trunk/java/org/apache/coyote/ActionCode.java
    tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
    tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties
    tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java
    tomcat/trunk/java/org/apache/coyote/http11/AbstractInputBuffer.java
    tomcat/trunk/java/org/apache/coyote/http11/InternalAprInputBuffer.java
    tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java
    tomcat/trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java
    tomcat/trunk/webapps/examples/WEB-INF/web.xml
    tomcat/trunk/webapps/examples/index.html

Modified: tomcat/trunk/java/org/apache/catalina/connector/Request.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Request.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/Request.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/Request.java Wed Feb  1 10:07:39 2012
@@ -75,6 +75,7 @@ import org.apache.catalina.realm.Generic
 import org.apache.catalina.util.ParameterMap;
 import org.apache.catalina.util.StringParser;
 import org.apache.coyote.ActionCode;
+import org.apache.coyote.http11.UpgradeInbound;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.ExceptionUtils;
@@ -2612,6 +2613,21 @@ public class Request
         return null;
     }
 
+
+    // --------------------------------------------------------- Upgrade Methods
+
+    public void doUpgrade(UpgradeInbound inbound)
+            throws IOException {
+
+        coyoteRequest.action(ActionCode.UPGRADE, inbound);
+
+        // Output required by RFC2616. Protocol specific headers should have
+        // already been set.
+        response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
+        response.flushBuffer();
+    }
+
+
     // ------------------------------------------------------ Protected Methods
 
 

Modified: tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java Wed Feb  1 10:07:39 2012
@@ -41,6 +41,7 @@ import javax.servlet.http.Part;
 
 import org.apache.catalina.Globals;
 import org.apache.catalina.security.SecurityUtil;
+import org.apache.coyote.http11.UpgradeInbound;
 import org.apache.tomcat.util.res.StringManager;
 
 /**
@@ -1085,4 +1086,20 @@ public class RequestFacade implements Ht
         return request.getConnector().getAllowTrace();
     }
 
+    /**
+     * Sets the response status to {@link
+     * HttpServletResponse.SC_SWITCHING_PROTOCOLS} and flushes the response.
+     * Protocol specific headers must have already been set before this method
+     * is called.
+     *
+     * @param inbound   The handler for all further incoming data on the current
+     *                  connection.
+     *
+     * @throws IOException  If the upgrade fails (e.g. if the response has
+     *                      already been committed.
+     */
+    public void doUpgrade(UpgradeInbound inbound)
+            throws IOException {
+        request.doUpgrade(inbound);
+    }
 }

Added: tomcat/trunk/java/org/apache/catalina/util/Conversions.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/util/Conversions.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/util/Conversions.java (added)
+++ tomcat/trunk/java/org/apache/catalina/util/Conversions.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,42 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+
+public class Conversions {
+
+    private Conversions() {
+        // Utility class. Hide default constructor.
+    }
+
+    public static long byteArrayToLong(byte[] input) throws IOException {
+        if (input.length > 8) {
+            // TODO: Better message
+            throw new IOException();
+        }
+
+        int shift = 0;
+        long result = 0;
+        for (int i = input.length; i < 0; i--) {
+            result = result + ((input[i] & 0xFF) << shift);
+            shift += 8;
+        }
+
+        return result;
+    }
+}

Added: tomcat/trunk/java/org/apache/catalina/websocket/Constants.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/websocket/Constants.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/Constants.java (added)
+++ tomcat/trunk/java/org/apache/catalina/websocket/Constants.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * Constants for this Java package.
+ */
+public class Constants {
+    public static final String Package = "org.apache.catalina.websocket";
+}

Added: tomcat/trunk/java/org/apache/catalina/websocket/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/websocket/LocalStrings.properties?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/LocalStrings.properties (added)
+++ tomcat/trunk/java/org/apache/catalina/websocket/LocalStrings.properties Wed Feb  1 10:07:39 2012
@@ -0,0 +1,14 @@
+# 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.
\ No newline at end of file

Added: tomcat/trunk/java/org/apache/catalina/websocket/MessageInbound.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/websocket/MessageInbound.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/MessageInbound.java (added)
+++ tomcat/trunk/java/org/apache/catalina/websocket/MessageInbound.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,61 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+public abstract class MessageInbound extends StreamInbound {
+
+    // TODO: Make buffer sizes configurable
+    // TODO: Allow buffers to expand
+    ByteBuffer bb = ByteBuffer.allocate(8192);
+    CharBuffer cb = CharBuffer.allocate(8192);
+
+    @Override
+    protected void onBinaryData(InputStream is) throws IOException {
+        int read = 0;
+        while (read > -1) {
+            bb.position(bb.position() + read);
+            read = is.read(bb.array(), bb.position(), bb.remaining());
+        }
+        bb.flip();
+        onBinaryMessage(bb);
+        bb.clear();
+    }
+
+    @Override
+    protected void onTextData(Reader r) throws IOException {
+        int read = 0;
+        while (read > -1) {
+            cb.position(cb.position() + read);
+            read = r.read(cb.array(), cb.position(), cb.remaining());
+        }
+        cb.limit(cb.position());
+        cb.position(0);
+        onTextMessage(cb);
+        cb.clear();
+    }
+
+    protected abstract void onBinaryMessage(ByteBuffer message)
+            throws IOException;
+    protected abstract void onTextMessage(CharBuffer message)
+            throws IOException;
+}

Added: tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java (added)
+++ tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,146 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import org.apache.catalina.util.Conversions;
+import org.apache.coyote.http11.UpgradeInbound;
+import org.apache.coyote.http11.UpgradeOutbound;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+
+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 InputStream is = null;
+    private WsOutbound outbound;
+
+    @Override
+    public void setUpgradeOutbound(UpgradeOutbound upgradeOutbound) {
+        outbound = new WsOutbound(upgradeOutbound);
+    }
+
+
+    @Override
+    public void setInputStream(InputStream is) {
+        this.is = is;
+    }
+
+    public WsOutbound getStreamOutbound() {
+        return outbound;
+    }
+
+    @Override
+    public SocketState onData() throws IOException {
+        // Must be start the start of a frame
+
+        // Read the first byte
+        int i = is.read();
+
+        fin = (i & 0x80) > 0;
+
+        rsv1 = (i & 0x40) > 0;
+        rsv2 = (i & 0x20) > 0;
+        rsv3 = (i & 0x10) > 0;
+
+        if (rsv1 || rsv2 || rsv3) {
+            // TODO: Not supported.
+        }
+
+        opCode = (i & 0x0F);
+        validateOpCode(opCode);
+
+        // Read the next byte
+        i = is.read();
+
+        // Client data must be masked and this isn't
+        if ((i & 0x80) == 0) {
+            // TODO: Better message
+            throw new IOException();
+        }
+
+        payloadLength = i & 0x7F;
+        if (payloadLength == 126) {
+            byte[] extended = new byte[2];
+            is.read(extended);
+            payloadLength = Conversions.byteArrayToLong(extended);
+        } else if (payloadLength == 127) {
+            byte[] extended = new byte[8];
+            is.read(extended);
+            payloadLength = Conversions.byteArrayToLong(extended);
+        }
+
+        byte[] mask = new byte[4];
+        is.read(mask);
+
+        if (opCode == 1 || opCode == 2) {
+            WsInputStream wsIs = new WsInputStream(is, mask, payloadLength);
+            if (opCode == 2) {
+                onBinaryData(wsIs);
+            } else {
+                InputStreamReader r =
+                        new InputStreamReader(wsIs, B2CConverter.UTF_8);
+                onTextData(r);
+            }
+        }
+
+        // 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.
+
+        // TODO: Handle control frames appearing in the middle of a multi-frame
+        //       message
+
+        return SocketState.UPGRADE;
+    }
+
+    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();
+        }
+    }
+}

Added: tomcat/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java (added)
+++ tomcat/trunk/java/org/apache/catalina/websocket/WebSocketServlet.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,147 @@
+/*
+ * 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.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.connector.RequestFacade;
+import org.apache.catalina.util.Base64;
+import org.apache.tomcat.util.buf.B2CConverter;
+
+/**
+ * Provides the base implementation of a Servlet for processing WebSocket
+ * connections as per RFC6455. It is expected that applications will extend this
+ * implementation and provide application specific functionality.
+ */
+public abstract class WebSocketServlet extends HttpServlet {
+
+    private static final long serialVersionUID = 1L;
+    private static final byte[] WS_ACCEPT =
+            "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(
+                    B2CConverter.ISO_8859_1);
+
+    private MessageDigest sha1Helper;
+
+
+    @Override
+    public void init() throws ServletException {
+        super.init();
+
+        try {
+            sha1Helper = MessageDigest.getInstance("SHA1");
+        } catch (NoSuchAlgorithmException e) {
+            throw new ServletException(e);
+        }
+    }
+
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+
+        // Information required to send the server handshake message
+        String key;
+        String subProtocol = null;
+        List<String> extensions = Collections.emptyList();
+
+        if (!headerContains(req, "upgrade", "websocket")) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return;
+        }
+
+        if (!headerContains(req, "connection", "upgrade")) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return;
+        }
+
+        if (!headerContains(req, "sec-websocket-version", "13")) {
+            resp.setStatus(426);
+            resp.setHeader("Sec-WebSocket-Version", "13");
+            return;
+        }
+
+        key = req.getHeader("Sec-WebSocket-Key");
+        if (key == null) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return;
+        }
+
+        // TODO Read client handshake - Origin
+        //                              Sec-WebSocket-Protocol
+        //                              Sec-WebSocket-Extensions
+
+        // TODO Extensions require the ability to specify something (API TBD)
+        //      that can be passed to the Tomcat internals and process extension
+        //      data present when the frame is fragmented.
+
+        // If we got this far, all is good. Accept the connection.
+        resp.setHeader("upgrade", "websocket");
+        resp.setHeader("connection", "upgrade");
+        resp.setHeader("Sec-WebSocket-Accept", getWebSocketAccept(key));
+        if (subProtocol != null) {
+            // TODO
+        }
+        if (!extensions.isEmpty()) {
+            // TODO
+        }
+
+        // Small hack until the Servlet API provides a way to do this.
+        StreamInbound inbound = createWebSocketInbound();
+        ((RequestFacade) req).doUpgrade(inbound);
+    }
+
+
+    private boolean headerContains(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())) {
+                    return true;
+                }
+            }
+        }
+        return true;
+    }
+
+
+    private String getWebSocketAccept(String key) {
+        synchronized (sha1Helper) {
+            sha1Helper.reset();
+            sha1Helper.update(key.getBytes(B2CConverter.ISO_8859_1));
+            return Base64.encode(sha1Helper.digest(WS_ACCEPT));
+        }
+    }
+
+    protected abstract StreamInbound createWebSocketInbound();
+}

Added: tomcat/trunk/java/org/apache/catalina/websocket/WsInputStream.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/websocket/WsInputStream.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/WsInputStream.java (added)
+++ tomcat/trunk/java/org/apache/catalina/websocket/WsInputStream.java Wed Feb  1 10:07:39 2012
@@ -0,0 +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 org.apache.catalina.websocket;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class WsInputStream extends java.io.InputStream {
+
+    private InputStream wrapped;
+    private byte[] mask;
+    private long remaining;
+    private long read;
+
+    public WsInputStream(InputStream wrapped, byte[] mask, long remaining) {
+        this.wrapped = wrapped;
+        this.mask = mask;
+        this.remaining = remaining;
+        this.read = 0;
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (remaining == 0) {
+            return -1;
+        }
+
+        remaining--;
+        read++;
+
+        int masked = wrapped.read();
+        return masked ^ mask[(int) ((read - 1) % 4)];
+    }
+
+}

Added: tomcat/trunk/java/org/apache/catalina/websocket/WsOutbound.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/websocket/WsOutbound.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/WsOutbound.java (added)
+++ tomcat/trunk/java/org/apache/catalina/websocket/WsOutbound.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,178 @@
+/*
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+import org.apache.coyote.http11.UpgradeOutbound;
+import org.apache.tomcat.util.buf.B2CConverter;
+
+public class WsOutbound {
+
+    private static final int DEFAULT_BUFFER_SIZE = 2048;
+
+    private UpgradeOutbound upgradeOutbound;
+    private ByteBuffer bb;
+    private CharBuffer cb;
+    protected Boolean text = null;
+    protected boolean firstFrame = true;
+
+
+    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.cb = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
+    }
+
+
+    public void writeBinaryData(int b) throws IOException {
+        if (bb.position() == bb.capacity()) {
+            doFlush(false);
+        }
+        if (text == null) {
+            text = Boolean.FALSE;
+        } else if (text == Boolean.TRUE) {
+            // Flush the character data
+            flush();
+            text = Boolean.FALSE;
+        }
+        bb.put((byte) (b & 0xFF));
+    }
+
+
+    public void writeTextData(char c) throws IOException {
+        if (cb.position() == cb.capacity()) {
+            doFlush(false);
+        }
+
+        if (text == null) {
+            text = Boolean.TRUE;
+        } else if (text == Boolean.FALSE) {
+            // Flush the binary data
+            flush();
+            text = Boolean.TRUE;
+        }
+        cb.append(c);
+    }
+
+
+    public void writeBinaryMessage(ByteBuffer msgBb) throws IOException {
+        if (text != null) {
+            // Empty the buffer
+            flush();
+        }
+        text = Boolean.FALSE;
+        doWriteBinary(msgBb, true);
+    }
+
+
+    public void writeTextMessage(CharBuffer msgCb) throws IOException {
+        if (text != null) {
+            // Empty the buffer
+            flush();
+        }
+        text = Boolean.TRUE;
+        doWriteText(msgCb, true);
+    }
+
+
+    public void flush() throws IOException {
+        doFlush(true);
+    }
+
+    private void doFlush(boolean finalFragment) throws IOException {
+        if (text == null) {
+            // No data
+            return;
+        }
+        if (text.booleanValue()) {
+            doWriteText(cb, finalFragment);
+        } else {
+            doWriteBinary(bb, finalFragment);
+        }
+    }
+
+
+    public void close() throws IOException {
+        doFlush(true);
+
+        // TODO: Send a close message
+        bb = null;
+        cb = null;
+        upgradeOutbound = null;
+    }
+
+
+    protected void doWriteBinary(ByteBuffer buffer, boolean finalFragment)
+            throws IOException {
+
+        // Prepare to write
+        buffer.flip();
+
+        // Work out the first byte
+        int first = 0x00;
+        if (finalFragment) {
+            first = first + 0x80;
+        }
+        if (firstFrame) {
+            if (text.booleanValue()) {
+                first = first + 0x1;
+            } else {
+                first = first + 0x2;
+            }
+        }
+        // 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 {
+            upgradeOutbound.write(126);
+            upgradeOutbound.write(buffer.limit() >>> 8);
+            upgradeOutbound.write(buffer.limit() & 0xFF);
+        }
+
+        // Write the content
+        upgradeOutbound.write(buffer.array(), 0, buffer.limit());
+        upgradeOutbound.flush();
+
+        // Reset
+        if (finalFragment) {
+            text = null;
+            firstFrame = true;
+        } else {
+            firstFrame = false;
+        }
+        bb.clear();
+    }
+
+
+    protected void doWriteText(CharBuffer buffer, boolean finalFragment)
+            throws IOException {
+        buffer.flip();
+        B2CConverter.UTF_8.newEncoder().encode(buffer, bb, true);
+        doWriteBinary(bb, finalFragment);
+        // Reset
+        cb.clear();
+    }
+}

Modified: tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java Wed Feb  1 10:07:39 2012
@@ -106,6 +106,8 @@ public abstract class AbstractProcessor<
 
     protected abstract boolean isComet();
 
+    protected abstract boolean isUpgrade();
+
     /**
      * Process HTTP requests. All requests are treated as HTTP requests to start
      * with although they may change type during processing.
@@ -113,7 +115,6 @@ public abstract class AbstractProcessor<
     public abstract SocketState process(SocketWrapper<S> socket)
         throws IOException;
 
-
     /**
      * Process in-progress Comet requests. These will start as HTTP requests.
      */
@@ -124,4 +125,10 @@ public abstract class AbstractProcessor<
      * requests.
      */
     public abstract SocketState asyncDispatch(SocketStatus status);
+
+    /**
+     * Processes data received on a connection that has been through an HTTP
+     * upgrade.
+     */
+    public abstract SocketState upgradeDispatch() throws IOException;
 }

Modified: tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java (original)
+++ tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java Wed Feb  1 10:07:39 2012
@@ -551,6 +551,8 @@ public abstract class AbstractProtocol i
                         state = processor.asyncDispatch(status);
                     } else if (processor.isComet()) {
                         state = processor.event(status);
+                    } else if (processor.isUpgrade()) {
+                        state = processor.upgradeDispatch();
                     } else {
                         state = processor.process(socket);
                     }
@@ -574,6 +576,9 @@ public abstract class AbstractProtocol i
                     // closed. If it works, the socket will be re-added to the
                     // poller
                     release(socket, processor, false, false);
+                } else if (state == SocketState.UPGRADE) {
+                    // Need to keep the connection associated with the processor
+                    longPoll(socket, processor);
                 } else {
                     // Connection closed. OK to recycle the processor.
                     release(socket, processor, true, false);

Modified: tomcat/trunk/java/org/apache/coyote/ActionCode.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ActionCode.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ActionCode.java (original)
+++ tomcat/trunk/java/org/apache/coyote/ActionCode.java Wed Feb  1 10:07:39 2012
@@ -188,5 +188,10 @@ public enum ActionCode {
     /**
      * Callback to determine if async is timing out
      */
-    ASYNC_IS_TIMINGOUT
+    ASYNC_IS_TIMINGOUT,
+
+    /**
+     * Callback to trigger the HTTP upgrade process.
+     */
+    UPGRADE
 }

Modified: tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java Wed Feb  1 10:07:39 2012
@@ -455,6 +455,9 @@ public abstract class AbstractAjpProcess
             ((AtomicBoolean) param).set(asyncStateMachine.isAsync());
         } else if (actionCode == ActionCode.ASYNC_IS_TIMINGOUT) {
             ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
+        } else if (actionCode == ActionCode.UPGRADE) {
+            // HTTP connections only. Unsupported for AJP.
+            // NOOP
         }  else {
             actionInternal(actionCode, param);
         }
@@ -509,6 +512,15 @@ public abstract class AbstractAjpProcess
                 sm.getString("ajpprocessor.comet.notsupported"));
     }
 
+
+    @Override
+    public SocketState upgradeDispatch() throws IOException {
+        // Should never reach this code but in case we do...
+        throw new IOException(
+                sm.getString("ajpprocessor.httpupgrade.notsupported"));
+    }
+
+
     /**
      * Recycle the processor, ready for the next request which may be on the
      * same connection or a different connection.
@@ -553,6 +565,13 @@ public abstract class AbstractAjpProcess
     }
 
 
+    @Override
+    protected final boolean isUpgrade() {
+        // AJP does not support HTTP upgrade
+        return false;
+    }
+
+
     /**
      * Get more request body data from the web server and store it in the
      * internal buffer.

Modified: tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/coyote/ajp/LocalStrings.properties Wed Feb  1 10:07:39 2012
@@ -32,6 +32,7 @@ ajpprocessor.request.prepare=Error prepa
 ajpprocessor.request.process=Error processing request
 ajpprocessor.certs.fail=Certificate conversion failed
 ajpprocessor.comet.notsupported=The Comet protocol is not supported by this connector
+ajpprocessor.httpupgrade.notsupported=HTTP upgrades are not supported by this connector
 
 ajpmessage.null=Cannot append null value
 ajpmessage.overflow=Overflow error for buffer adding {0} bytes at position {1}

Modified: tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java Wed Feb  1 10:07:39 2012
@@ -253,6 +253,13 @@ public abstract class AbstractHttp11Proc
     protected String server = null;
 
 
+    /**
+     * Listener to which data available events are passed once the associated
+     * connection has completed the HTTP upgrade process.
+     */
+    protected UpgradeInbound upgradeInbound = null;
+
+
     public AbstractHttp11Processor(AbstractEndpoint endpoint) {
         super(endpoint);
     }
@@ -826,6 +833,15 @@ public abstract class AbstractHttp11Proc
             ((AtomicBoolean) param).set(asyncStateMachine.isAsync());
         } else if (actionCode == ActionCode.ASYNC_IS_TIMINGOUT) {
             ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
+        } else if (actionCode == ActionCode.UPGRADE) {
+            upgradeInbound = (UpgradeInbound) param;
+            upgradeInbound.setInputStream(
+                    new UpgradeInputStream(getInputBuffer()));
+            upgradeInbound.setUpgradeOutbound(
+                    new UpgradeOutbound(
+                            new UpgradeOutputStream(getOutputBuffer())));
+            // Stop further HTTP output
+            getOutputBuffer().finished = true;
         } else {
             actionInternal(actionCode, param);
         }
@@ -901,7 +917,7 @@ public abstract class AbstractHttp11Proc
         }
 
         while (!error && keepAlive && !comet && !isAsync() &&
-                !endpoint.isPaused()) {
+                upgradeInbound == null && !endpoint.isPaused()) {
 
             // Parsing the request header
             try {
@@ -1049,6 +1065,9 @@ public abstract class AbstractHttp11Proc
             return SocketState.CLOSED;
         } else if (isAsync() || comet) {
             return SocketState.LONG;
+        } else if (isUpgrade()) {
+            // May be data on the connection to process
+            return upgradeDispatch();
         } else {
             if (sendfileInProgress) {
                 return SocketState.SENDFILE;
@@ -1548,6 +1567,34 @@ public abstract class AbstractHttp11Proc
     }
 
 
+    @Override
+    public boolean isUpgrade() {
+        return upgradeInbound != null;
+    }
+
+
+
+    @Override
+    public SocketState upgradeDispatch() throws IOException {
+        SocketState result = upgradeInbound.onData();
+        AbstractInputBuffer<S> ib = getInputBuffer();
+        while (result == SocketState.UPGRADE) {
+            // Check to see if there is more data to process
+            if (ib.available() == 0) {
+                // Read any data that might be available
+                // Note: This will block for BIO regardless
+                ib.fill(false);
+            }
+            if (ib.available() == 0) {
+                // Still no data available, exit this loop
+                break;
+            }
+            result = upgradeInbound.onData();
+        }
+        return result;
+    }
+
+
     /**
      * Provides a mechanism for those connector implementations (currently only
      * NIO) that need to reset timeouts from Async timeouts to standard HTTP
@@ -1605,6 +1652,7 @@ public abstract class AbstractHttp11Proc
         getInputBuffer().recycle();
         getOutputBuffer().recycle();
         asyncStateMachine.recycle();
+        upgradeInbound = null;
         remoteAddr = null;
         remoteHost = null;
         localAddr = null;

Modified: tomcat/trunk/java/org/apache/coyote/http11/AbstractInputBuffer.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractInputBuffer.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/AbstractInputBuffer.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/AbstractInputBuffer.java Wed Feb  1 10:07:39 2012
@@ -310,8 +310,23 @@ public abstract class AbstractInputBuffe
 
     }
 
-    // ---------------------------------------------------- InputBuffer Methods
 
+    /**
+     * Available bytes in the buffers (note that due to encoding, this may not
+     * correspond).
+     */
+    public int available() {
+        int result = (lastValid - pos);
+        if ((result == 0) && (lastActiveFilter >= 0)) {
+            for (int i = 0; (result == 0) && (i <= lastActiveFilter); i++) {
+                result = activeFilters[i].available();
+            }
+        }
+        return result;
+    }
+
+
+    // ---------------------------------------------------- InputBuffer Methods
 
     /**
      * Read some bytes.

Modified: tomcat/trunk/java/org/apache/coyote/http11/InternalAprInputBuffer.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/InternalAprInputBuffer.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/InternalAprInputBuffer.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/InternalAprInputBuffer.java Wed Feb  1 10:07:39 2012
@@ -518,20 +518,6 @@ public class InternalAprInputBuffer exte
     }
 
 
-    /**
-     * Available bytes (note that due to encoding, this may not correspond )
-     */
-    public int available() {
-        int result = (lastValid - pos);
-        if ((result == 0) && (lastActiveFilter >= 0)) {
-            for (int i = 0; (result == 0) && (i <= lastActiveFilter); i++) {
-                result = activeFilters[i].available();
-            }
-        }
-        return result;
-    }
-
-
     // ---------------------------------------------------- InputBuffer Methods
 
 

Modified: tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java Wed Feb  1 10:07:39 2012
@@ -763,20 +763,6 @@ public class InternalNioInputBuffer exte
     }
 
 
-    /**
-     * Available bytes (note that due to encoding, this may not correspond )
-     */
-    public int available() {
-        int result = (lastValid - pos);
-        if ((result == 0) && (lastActiveFilter >= 0)) {
-            for (int i = 0; (result == 0) && (i <= lastActiveFilter); i++) {
-                result = activeFilters[i].available();
-            }
-        }
-        return result;
-    }
-
-
     // ------------------------------------------------------ Protected Methods
 
     @Override

Added: tomcat/trunk/java/org/apache/coyote/http11/UpgradeInbound.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/UpgradeInbound.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/UpgradeInbound.java (added)
+++ tomcat/trunk/java/org/apache/coyote/http11/UpgradeInbound.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+
+/**
+ * Receives notification that there is data to be read on the upgraded
+ * connection and processes it.
+ *
+ * TODO: Move this to a more appropriate package (TBD).
+ */
+public interface UpgradeInbound {
+
+    void setInputStream(InputStream is);
+
+    SocketState onData() throws IOException;
+
+    void setUpgradeOutbound(UpgradeOutbound upgradeOutbound);
+}

Added: tomcat/trunk/java/org/apache/coyote/http11/UpgradeInputStream.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/UpgradeInputStream.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/UpgradeInputStream.java (added)
+++ tomcat/trunk/java/org/apache/coyote/http11/UpgradeInputStream.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.ByteChunk.ByteInputChannel;
+
+/**
+ * Provides a buffered {@link InputStream} to read data from the upgraded
+ * connection.
+ *
+ * TODO: Override a few more {@link InputStream} methods for efficiency.
+ *
+ * Based on a combination of {@link org.apache.catalina.connector.InputBuffer}
+ * and (@link CoyoteInputStream}.
+ */
+public class UpgradeInputStream extends InputStream
+        implements ByteInputChannel {
+
+    private InputBuffer ib = null;
+    private ByteChunk bb = new ByteChunk(8192);
+
+    public UpgradeInputStream(InputBuffer ib) {
+        this.ib = ib;
+        bb.setByteInputChannel(this);
+    }
+
+    @Override
+    public int read() throws IOException {
+        return bb.substract();
+    }
+
+    @Override
+    public int realReadBytes(byte[] cbuf, int off, int len) throws IOException {
+        return ib.doRead(bb, null);
+    }
+}

Added: tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutbound.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutbound.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutbound.java (added)
+++ tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutbound.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Allows data to be written to the upgraded connection.
+ *
+ * TODO: Move this to a more appropriate package (TBD).
+ *
+ * TODO: Override more methods for efficiency.
+ */
+public class UpgradeOutbound extends OutputStream {
+
+    @Override
+    public void flush() throws IOException {
+        os.flush();
+    }
+
+    private OutputStream os;
+
+    public UpgradeOutbound(OutputStream os) {
+        this.os = os;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        os.write(b);
+    }
+}

Added: tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutputStream.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutputStream.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutputStream.java (added)
+++ tomcat/trunk/java/org/apache/coyote/http11/UpgradeOutputStream.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.ByteChunk.ByteOutputChannel;
+
+public class UpgradeOutputStream extends OutputStream
+        implements ByteOutputChannel{
+
+    private AbstractOutputBuffer<?> ob = null;
+    private ByteChunk bb = new ByteChunk(8192);
+
+    public UpgradeOutputStream(AbstractOutputBuffer<?> ob) {
+        this.ob = ob;
+        bb.setByteOutputChannel(this);
+    }
+
+
+    @Override
+    public void realWriteBytes(byte[] cbuf, int off, int len)
+            throws IOException {
+        ob.committed = true;
+        ob.doWrite(bb, null);
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        bb.append((byte) b);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        bb.flushBuffer();
+        ob.flush();
+    }
+}

Modified: tomcat/trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java Wed Feb  1 10:07:39 2012
@@ -55,7 +55,7 @@ public abstract class AbstractEndpoint {
         public enum SocketState {
             // TODO Add a new state to the AsyncStateMachine and remove
             //      ASYNC_END (if possible)
-            OPEN, CLOSED, LONG, ASYNC_END, SENDFILE
+            OPEN, CLOSED, LONG, ASYNC_END, SENDFILE, UPGRADE
         }
 
 

Added: tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java (added)
+++ tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,76 @@
+/*
+ * 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.apache.catalina.startup.TomcatBaseTest;
+
+public class TestWebSocket extends TomcatBaseTest {
+
+    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
+        }
+    }
+}

Added: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java (added)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java Wed Feb  1 10:07:39 2012
@@ -0,0 +1,50 @@
+/*
+ * 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);
+        }
+    }
+}

Added: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java (added)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/EchoStream.java Wed Feb  1 10:07:39 2012
@@ -0,0 +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();
+        }
+    }
+}

Modified: tomcat/trunk/webapps/examples/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/web.xml?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/web.xml (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/web.xml Wed Feb  1 10:07:39 2012
@@ -347,4 +347,22 @@
       <url-pattern>/async/stockticker</url-pattern>
     </servlet-mapping>
 
+    <!-- WebSocket Examples -->
+    <servlet>
+      <servlet-name>wsEchoStream</servlet-name>
+      <servlet-class>websocket.EchoStream</servlet-class>
+    </servlet>
+    <servlet-mapping>
+      <servlet-name>wsEchoStream</servlet-name>
+      <url-pattern>/websocket/echoStream</url-pattern>
+    </servlet-mapping>
+    <servlet>
+      <servlet-name>wsEchoMessage</servlet-name>
+      <servlet-class>websocket.EchoMessage</servlet-class>
+    </servlet>
+    <servlet-mapping>
+      <servlet-name>wsEchoMessage</servlet-name>
+      <url-pattern>/websocket/echoMessage</url-pattern>
+    </servlet-mapping>
+
 </web-app>

Modified: tomcat/trunk/webapps/examples/index.html
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/index.html?rev=1239034&r1=1239033&r2=1239034&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/index.html (original)
+++ tomcat/trunk/webapps/examples/index.html Wed Feb  1 10:07:39 2012
@@ -25,5 +25,6 @@
 <ul>
 <li><a href="servlets">Servlets examples</a></li>
 <li><a href="jsp">JSP Examples</a></li>
+<li><a href="websocket">WebSocket Examples</a></li>
 </ul>
 </BODY></HTML>

Added: tomcat/trunk/webapps/examples/websocket/echo.html
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/websocket/echo.html?rev=1239034&view=auto
==============================================================================
--- tomcat/trunk/webapps/examples/websocket/echo.html (added)
+++ tomcat/trunk/webapps/examples/websocket/echo.html Wed Feb  1 10:07:39 2012
@@ -0,0 +1,54 @@
+<!--
+  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>
+<html>
+<head>
+<title>Apache Tomcat WebSocket Examples: Echo</title>
+<script type="text/javascript">
+function echo(target) {
+  if ("WebSocket" in window) {
+    // TODO: Can we use relative URLs?
+    var ws = new WebSocket(target);
+    ws.onopen = function() {
+      ws.send("Connection opened");
+      alert("WebSocket connection opened.");
+      ws.send("Here is a message!");
+    }
+    ws.onmessage = function(event) {
+      alert("Received: " + event.data);
+    }
+    ws.onclose = function() {
+      alert("WebSocket connection closed.");
+    }
+    // TODO: Extend with a text box for users to enter data
+  } else {
+    alert("WebSocket is not supported by this browser.");
+  }
+}
+</script>
+</head>
+<body>
+<div id="echoStreamTest">
+<a href="javascript:echo('ws://localhost:8080/examples/websocket/echoStream')">
+Start echo example using streams on the server side</a>
+</div>
+<div id="echoStreamMessage">
+<a href="javascript:echo('ws://localhost:8080/examples/websocket/echoMessage')">
+Start echo example using messages on the server side</a>
+</div>
+</body>
+</html>
\ No newline at end of file

Copied: tomcat/trunk/webapps/examples/websocket/index.html (from r1239024, tomcat/trunk/webapps/examples/index.html)
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/websocket/index.html?p2=tomcat/trunk/webapps/examples/websocket/index.html&p1=tomcat/trunk/webapps/examples/index.html&r1=1239024&r2=1239034&rev=1239034&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/index.html (original)
+++ tomcat/trunk/webapps/examples/websocket/index.html Wed Feb  1 10:07:39 2012
@@ -1,29 +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 Examples</TITLE>
-<META http-equiv=Content-Type content="text/html">
-</HEAD>
-<BODY>
-<P>
-<H3>Apache Tomcat Examples</H3>
-<P></P>
-<ul>
-<li><a href="servlets">Servlets examples</a></li>
-<li><a href="jsp">JSP Examples</a></li>
-</ul>
-</BODY></HTML>
+<!--
+  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


Mime
View raw message