From 2cc4fbb5ee1fd1e716b8c108b6605f2777e974d1 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 28 Feb 2011 09:45:41 +0000 Subject: [PATCH] 337685 Work in progress on draft 6 websockets git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2831 7e9141cc-0065-0410-87d8-b60c137991c4 --- VERSION.txt | 2 +- .../eclipse/jetty/client/HttpConnection.java | 18 +- .../eclipse/jetty/client/HttpDestination.java | 3 +- .../eclipse/jetty/client/HttpExchange.java | 5 +- .../jetty/client/HttpExchangeTest.java | 3 +- .../org/eclipse/jetty/io/AbstractBuffer.java | 1 + .../eclipse/jetty/io/AbstractConnection.java | 51 ++ .../java/org/eclipse/jetty/io/Connection.java | 11 + .../jetty/io/nio/SelectChannelEndPoint.java | 9 +- .../eclipse/jetty/server/HttpConnection.java | 27 +- .../jetty/server/handler/ConnectHandler.java | 104 ++-- .../org/eclipse/jetty/util/StringUtil.java | 9 + .../jetty/websocket/FrameHandlerD5.java | 94 ---- .../eclipse/jetty/websocket/WebSocket.java | 19 +- .../jetty/websocket/WebSocketConnection.java | 2 +- .../websocket/WebSocketConnectionD00.java | 21 +- .../websocket/WebSocketConnectionD05.java | 373 ------------- .../websocket/WebSocketConnectionD06.java | 481 +++++++++++++++++ .../jetty/websocket/WebSocketFactory.java | 17 +- .../jetty/websocket/WebSocketGenerator.java | 2 +- .../websocket/WebSocketGeneratorD00.java | 4 +- .../websocket/WebSocketGeneratorD01.java | 10 +- ...torD05.java => WebSocketGeneratorD06.java} | 132 +++-- .../jetty/websocket/WebSocketHandler.java | 6 +- ...ParserD05.java => WebSocketParserD06.java} | 10 +- .../jetty/websocket/WebSocketServlet.java | 6 +- .../websocket/WebSocketGeneratorD06Test.java | 198 +++++++ .../websocket/WebSocketMessageD01Test.java | 5 - .../websocket/WebSocketMessageD06Test.java | 488 ++++++++++++++++++ ...5Test.java => WebSocketParserD06Test.java} | 22 +- 30 files changed, 1459 insertions(+), 674 deletions(-) create mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java delete mode 100644 jetty-websocket/src/main/java/org/eclipse/jetty/websocket/FrameHandlerD5.java delete mode 100644 jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD05.java create mode 100644 jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java rename jetty-websocket/src/main/java/org/eclipse/jetty/websocket/{WebSocketGeneratorD05.java => WebSocketGeneratorD06.java} (58%) rename jetty-websocket/src/main/java/org/eclipse/jetty/websocket/{WebSocketParserD05.java => WebSocketParserD06.java} (97%) create mode 100644 jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06Test.java create mode 100644 jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD06Test.java rename jetty-websocket/src/test/java/org/eclipse/jetty/websocket/{WebSocketParserD05Test.java => WebSocketParserD06Test.java} (93%) diff --git a/VERSION.txt b/VERSION.txt index 1b5bfa9407f..55b5eab8275 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -11,7 +11,7 @@ jetty-7.3.1-SNAPSHOT + 337270 Shared Timer for session management + 337271 Flush SSL endpoint when dispatch thread held forever + 337678 Readded optional async connection mode for HttpClient - + 337685 Work in progress on draft 5 websockets + + 337685 Work in progress on draft 6 websockets + 337746 Fixed Session deIdle recursion + 337784 Improve HashSessionManager for session migrations + 337878 Extra tests of security constraints diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java index 63c60b8d97c..3c4fa833e4c 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpSchemes; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersions; +import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffers; @@ -44,10 +45,9 @@ import org.eclipse.jetty.util.thread.Timeout; * * @version $Revision: 879 $ $Date: 2009-09-11 16:13:28 +0200 (Fri, 11 Sep 2009) $ */ -public class HttpConnection implements Connection +public class HttpConnection extends AbstractConnection implements Connection { private HttpDestination _destination; - private EndPoint _endp; private HttpGenerator _generator; private HttpParser _parser; private boolean _http11 = true; @@ -56,6 +56,7 @@ public class HttpConnection implements Connection private Buffer _requestContentChunk; private boolean _requestComplete; private boolean _reserved; + // The current exchange waiting for a response private volatile HttpExchange _exchange; private HttpExchange _pipeline; @@ -64,6 +65,7 @@ public class HttpConnection implements Connection public void dump() throws IOException { + // TODO update to dumpable Log.info("endp=" + _endp + " " + _endp.isBufferingInput() + " " + _endp.isBufferingOutput()); Log.info("generator=" + _generator); Log.info("parser=" + _parser.getState() + " " + _parser.isMoreInBuffer()); @@ -74,16 +76,11 @@ public class HttpConnection implements Connection HttpConnection(Buffers requestBuffers, Buffers responseBuffers, EndPoint endp) { - _endp = endp; + super(endp); _generator = new HttpGenerator(requestBuffers,endp); _parser = new HttpParser(responseBuffers,endp,new Handler()); } - public long getTimeStamp() - { - return -1; - } - public void setReserved (boolean reserved) { _reserved = reserved; @@ -442,11 +439,6 @@ public class HttpConnection implements Connection { } - public EndPoint getEndPoint() - { - return _endp; - } - private void commitRequest() throws IOException { synchronized (this) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java index 3e31a896b86..3135e44bdf2 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.PathMap; +import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.EndPoint; @@ -312,7 +313,7 @@ public class HttpDestination public void onNewConnection(final HttpConnection connection) throws IOException { - HttpConnection q_connection = null; + AbstractConnection q_connection = null; synchronized (this) { diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java index 48b72eb2904..3f758a428cf 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java @@ -26,6 +26,7 @@ import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersions; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.BufferCache.CachedBuffer; +import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -690,9 +691,9 @@ public class HttpExchange return this._connection != null; } - HttpConnection disassociate() + AbstractConnection disassociate() { - HttpConnection result = _connection; + AbstractConnection result = _connection; this._connection = null; if (getStatus() == STATUS_CANCELLING) setStatus(STATUS_CANCELLED); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpExchangeTest.java index 7a4857b86f8..3f88d5177c6 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpExchangeTest.java @@ -30,6 +30,7 @@ import junit.framework.TestCase; import org.eclipse.jetty.client.security.ProxyAuthorization; import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.EofException; @@ -473,7 +474,7 @@ public class HttpExchangeTest extends TestCase //try to get a connection, and only wait 500ms, as we have //already reserved the max, should return null - org.eclipse.jetty.client.HttpConnection c = destination.reserveConnection(500); + AbstractConnection c = destination.reserveConnection(500); assertNull(c); //unreserve first connection diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java index e2e43804889..38842622dcb 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java @@ -636,6 +636,7 @@ public abstract class AbstractBuffer implements Buffer if (bytes!=null) return new String(bytes,getIndex(),length(),charset); return new String(asArray(), 0, length(),charset); + } catch(Exception e) { diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java new file mode 100644 index 00000000000..c957da76eec --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java @@ -0,0 +1,51 @@ +package org.eclipse.jetty.io; + +import java.io.IOException; + +import org.eclipse.jetty.util.log.Log; + + +public abstract class AbstractConnection implements Connection +{ + private final long _timeStamp; + protected final EndPoint _endp; + + public AbstractConnection(EndPoint endp) + { + _endp=endp; + _timeStamp = System.currentTimeMillis(); + } + + public AbstractConnection(EndPoint endp,long timestamp) + { + _endp=endp; + _timeStamp = timestamp; + } + + public long getTimeStamp() + { + return _timeStamp; + } + + public EndPoint getEndPoint() + { + return _endp; + } + + public void idleExpired() + { + try + { + _endp.close(); + } + catch(IOException e) + { + Log.ignore(e); + } + } + + public String toString() + { + return super.toString()+"@"+_endp.getLocalAddr()+":"+_endp.getLocalPort()+"<->"+_endp.getRemoteAddr()+":"+_endp.getRemotePort(); + } +} \ No newline at end of file diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java index 7a72754a616..e5961c86474 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java @@ -38,10 +38,21 @@ public interface Connection */ Connection handle() throws IOException; + /** + * @return the timestamp at which the connection was created + */ long getTimeStamp(); boolean isIdle(); boolean isSuspended(); + /** + * Called when the connection idle timeout expires + */ + void idleExpired(); + + /** + * Called when the connection is closed + */ void closed(); } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java index 934152895ae..219b1baacc8 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java @@ -248,14 +248,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo /* ------------------------------------------------------------ */ protected void idleExpired() { - try - { - close(); - } - catch (IOException e) - { - Log.ignore(e); - } + _connection.idleExpired(); } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index bb1603d873f..535f4175772 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -38,6 +38,7 @@ import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersions; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.Parser; +import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.BufferCache.CachedBuffer; @@ -86,17 +87,15 @@ import org.eclipse.jetty.util.thread.Timeout; *

* */ -public class HttpConnection implements Connection +public class HttpConnection extends AbstractConnection implements Connection { private static final int UNKNOWN = -2; private static final ThreadLocal __currentConnection = new ThreadLocal(); - private final long _timeStamp=System.currentTimeMillis(); private int _requests; private volatile boolean _handling; protected final Connector _connector; - protected final EndPoint _endp; protected final Server _server; protected final HttpURI _uri; @@ -143,9 +142,9 @@ public class HttpConnection implements Connection */ public HttpConnection(Connector connector, EndPoint endpoint, Server server) { + super(endpoint); _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET); _connector = connector; - _endp = endpoint; HttpBuffers ab = (HttpBuffers)_connector; _parser = new HttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler()); _requestFields = new HttpFields(); @@ -161,9 +160,9 @@ public class HttpConnection implements Connection protected HttpConnection(Connector connector, EndPoint endpoint, Server server, Parser parser, Generator generator, Request request) { + super(endpoint); _uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET); _connector = connector; - _endp = endpoint; _parser = parser; _requestFields = new HttpFields(); _responseFields = new HttpFields(server.getMaxCookieVersion()); @@ -198,15 +197,6 @@ public class HttpConnection implements Connection return _server; } - /* ------------------------------------------------------------ */ - /** - * @return The time this connection was established. - */ - public long getTimeStamp() - { - return _timeStamp; - } - /* ------------------------------------------------------------ */ /** * @return Returns the associatedObject. @@ -278,15 +268,6 @@ public class HttpConnection implements Connection return false; } - /* ------------------------------------------------------------ */ - /** - * @return The {@link EndPoint} for this connection. - */ - public EndPoint getEndPoint() - { - return _endp; - } - /* ------------------------------------------------------------ */ /** * @return false (this method is not yet implemented) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java index d5d018f57fa..f6be919abdc 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java @@ -15,6 +15,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.io.Buffer; @@ -279,10 +280,9 @@ public class ConnectHandler extends HandlerWrapper private ClientToProxyConnection prepareConnections(ConcurrentMap context, SocketChannel channel, Buffer buffer) { HttpConnection httpConnection = HttpConnection.getCurrentConnection(); - ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer); ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp()); + ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer,clientToProxy); clientToProxy.setConnection(proxyToServer); - proxyToServer.setConnection(clientToProxy); return clientToProxy; } @@ -307,9 +307,9 @@ public class ConnectHandler extends HandlerWrapper return new ClientToProxyConnection(context, channel, endPoint, timeStamp); } - protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap context, Buffer buffer) + protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap context, Buffer buffer,ClientToProxyConnection toProxy) { - return new ProxyToServerConnection(context, buffer); + return new ProxyToServerConnection(context, buffer,toProxy); } private SocketChannel connectToServer(HttpServletRequest request, String host, int port) throws IOException @@ -413,7 +413,7 @@ public class ConnectHandler extends HandlerWrapper protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey selectionKey) throws IOException { SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, selectionKey); - // TODO endp.setMaxIdleTime(_writeTimeout); + endp.setMaxIdleTime(_writeTimeout); return endp; } @@ -421,8 +421,7 @@ public class ConnectHandler extends HandlerWrapper protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint) { ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment(); - proxyToServer.setTimeStamp(System.currentTimeMillis()); - proxyToServer.setEndPoint(endpoint); + proxyToServer = new ProxyToServerConnection(endpoint,proxyToServer); return proxyToServer; } @@ -450,28 +449,43 @@ public class ConnectHandler extends HandlerWrapper } } - public class ProxyToServerConnection implements Connection + public class ProxyToServerConnection extends AbstractConnection implements Connection { - private final CountDownLatch _ready = new CountDownLatch(1); - private final Buffer _buffer = new IndirectNIOBuffer(1024); + private final CountDownLatch _ready ; + private final Buffer _buffer; private final ConcurrentMap _context; + private final ClientToProxyConnection _toClient; private volatile Buffer _data; - private volatile ClientToProxyConnection _toClient; - private volatile long _timestamp; - private volatile SelectChannelEndPoint _endPoint; - public ProxyToServerConnection(ConcurrentMap context, Buffer data) + public ProxyToServerConnection(ConcurrentMap context, Buffer data, ClientToProxyConnection toClient) { + super(null); _context = context; _data = data; + _ready = new CountDownLatch(1); + _buffer = new IndirectNIOBuffer(1024); + _toClient=toClient; + } + + public ProxyToServerConnection(EndPoint endp,ProxyToServerConnection connection) + { + super(endp); + _ready=connection._ready; + _buffer=connection._buffer; + _context=connection._context; + _data=connection._data; + _toClient=connection._toClient; + if (_toClient!=null) + _toClient.setConnection(this); + } @Override public String toString() { StringBuilder builder = new StringBuilder("ProxyToServer"); - builder.append("(:").append(_endPoint.getLocalPort()); - builder.append("<=>:").append(_endPoint.getRemotePort()); + builder.append("(:").append(_endp.getLocalPort()); + builder.append("<=>:").append(_endp.getRemotePort()); return builder.append(")").toString(); } @@ -482,18 +496,18 @@ public class ConnectHandler extends HandlerWrapper { if (_data != null) { - int written = write(_endPoint, _data, _context); + int written = write(_endp, _data, _context); _logger.debug("{}: written to server {} bytes", this, written); _data = null; } while (true) { - int read = read(_endPoint, _buffer, _context); + int read = read(_endp, _buffer, _context); if (read == -1) { - _logger.debug("{}: server closed connection {}", this, _endPoint); + _logger.debug("{}: server closed connection {}", this, _endp); close(); break; } @@ -501,8 +515,8 @@ public class ConnectHandler extends HandlerWrapper if (read == 0) break; - _logger.debug("{}: read from server {} bytes {}", this, read, _endPoint); - int written = write(_toClient._endPoint, _buffer, _context); + _logger.debug("{}: read from server {} bytes {}", this, read, _endp); + int written = write(_toClient.getEndPoint(), _buffer, _context); _logger.debug("{}: written to {} {} bytes", this, _toClient, written); } return this; @@ -530,26 +544,6 @@ public class ConnectHandler extends HandlerWrapper } } - public void setConnection(ClientToProxyConnection connection) - { - _toClient = connection; - } - - public long getTimeStamp() - { - return _timestamp; - } - - public void setTimeStamp(long timestamp) - { - _timestamp = timestamp; - } - - public void setEndPoint(SelectChannelEndPoint endpoint) - { - _endPoint = endpoint; - } - public boolean isIdle() { return false; @@ -588,7 +582,7 @@ public class ConnectHandler extends HandlerWrapper public void closeServer() throws IOException { - _endPoint.close(); + _endp.close(); } public void close() @@ -613,30 +607,27 @@ public class ConnectHandler extends HandlerWrapper } } - public class ClientToProxyConnection implements Connection + public class ClientToProxyConnection extends AbstractConnection implements Connection { private final Buffer _buffer = new IndirectNIOBuffer(1024); private final ConcurrentMap _context; private final SocketChannel _channel; - private final EndPoint _endPoint; - private final long _timestamp; private volatile ProxyToServerConnection _toServer; private boolean _firstTime = true; public ClientToProxyConnection(ConcurrentMap context, SocketChannel channel, EndPoint endPoint, long timestamp) { + super(endPoint,timestamp); _context = context; _channel = channel; - _endPoint = endPoint; - _timestamp = timestamp; } @Override public String toString() { StringBuilder builder = new StringBuilder("ClientToProxy"); - builder.append("(:").append(_endPoint.getLocalPort()); - builder.append("<=>:").append(_endPoint.getRemotePort()); + builder.append("(:").append(_endp.getLocalPort()); + builder.append("<=>:").append(_endp.getRemotePort()); return builder.append(")").toString(); } @@ -654,11 +645,11 @@ public class ConnectHandler extends HandlerWrapper while (true) { - int read = read(_endPoint, _buffer, _context); + int read = read(_endp, _buffer, _context); if (read == -1) { - _logger.debug("{}: client closed connection {}", this, _endPoint); + _logger.debug("{}: client closed connection {}", this, _endp); close(); break; } @@ -666,8 +657,8 @@ public class ConnectHandler extends HandlerWrapper if (read == 0) break; - _logger.debug("{}: read from client {} bytes {}", this, read, _endPoint); - int written = write(_toServer._endPoint, _buffer, _context); + _logger.debug("{}: read from client {} bytes {}", this, read, _endp); + int written = write(_toServer.getEndPoint(), _buffer, _context); _logger.debug("{}: written to {} {} bytes", this, _toServer, written); } return this; @@ -696,11 +687,6 @@ public class ConnectHandler extends HandlerWrapper } } - public long getTimeStamp() - { - return _timestamp; - } - public boolean isIdle() { return false; @@ -722,7 +708,7 @@ public class ConnectHandler extends HandlerWrapper public void closeClient() throws IOException { - _endPoint.close(); + _endp.close(); } public void closeServer() throws IOException diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java index e0388946479..3effaa317a3 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.util; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import org.eclipse.jetty.util.log.Log; @@ -39,6 +40,14 @@ public class StringUtil public final static String __UTF8Alt="UTF8"; public final static String __UTF16="UTF-16"; + public final static Charset __UTF8_CHARSET; + public final static Charset __ISO_8859_1_CHARSET; + + static + { + __UTF8_CHARSET=Charset.forName(__UTF8); + __ISO_8859_1_CHARSET=Charset.forName(__ISO_8859_1); + } private static char[] lowercases = { '\000','\001','\002','\003','\004','\005','\006','\007', diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/FrameHandlerD5.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/FrameHandlerD5.java deleted file mode 100644 index 3169b1e8c7e..00000000000 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/FrameHandlerD5.java +++ /dev/null @@ -1,94 +0,0 @@ -// ======================================================================== -// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== - -package org.eclipse.jetty.websocket; - -import org.eclipse.jetty.io.Buffer; -import org.eclipse.jetty.util.Utf8StringBuilder; -import org.eclipse.jetty.util.log.Log; - -final class FrameHandlerD5 implements WebSocketParser.FrameHandler -{ - public final static byte PING=1; - public final static byte PONG=1; - - final WebSocketConnectionD05 _connection; - final WebSocket _websocket; - final Utf8StringBuilder _utf8 = new Utf8StringBuilder(); - boolean _fragmented=false; - - FrameHandlerD5(WebSocketConnectionD05 connection, WebSocket websocket) - { - _connection=connection; - _websocket=websocket; - } - - public void onFrame(boolean more, byte flags, byte opcode, Buffer buffer) - { - try - { - byte[] array=buffer.array(); - - if (opcode==0) - { - if (more) - { - _utf8.append(buffer.array(),buffer.getIndex(),buffer.length()); - _fragmented=true; - } - else if (_fragmented) - { - _utf8.append(buffer.array(),buffer.getIndex(),buffer.length()); - _websocket.onMessage(opcode,_utf8.toString()); - _utf8.reset(); - _fragmented=false; - } - else - { - _websocket.onMessage(opcode,buffer.toString("utf-8")); - } - } - else if (opcode==PING) - { - _connection.sendMessage(PONG,buffer.array(),buffer.getIndex(),buffer.length()); - } - else if (opcode==PONG) - { - - } - else - { - if (more) - { - _websocket.onFragment(true,opcode,array,buffer.getIndex(),buffer.length()); - } - else if (_fragmented) - { - _websocket.onFragment(false,opcode,array,buffer.getIndex(),buffer.length()); - } - else - { - _websocket.onMessage(opcode,array,buffer.getIndex(),buffer.length()); - } - } - } - catch(ThreadDeath th) - { - throw th; - } - catch(Throwable th) - { - Log.warn(th); - } - } -} \ No newline at end of file diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java index 152d4c2a35b..87cbf9cc718 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java @@ -17,13 +17,29 @@ import java.io.IOException; public interface WebSocket { + @Deprecated public final byte LENGTH_FRAME=(byte)0x80; + @Deprecated public final byte SENTINEL_FRAME=(byte)0x00; + + public final static byte OP_CONTINUATION = 0x00; + public final static byte OP_CLOSE = 0x01; + public final static byte OP_PING = 0x02; + public final static byte OP_PONG = 0x03; + public final static byte OP_TEXT = 0x04; + public final static byte OP_BINARY = 0x05; + + public final static int CLOSE_NORMAL=1000; + public final static int CLOSE_SHUTDOWN=1001; + public final static int CLOSE_PROTOCOL=1002; + public final static int CLOSE_DATA=1003; + public final static int CLOSE_LARGE=1004; + void onConnect(Outbound outbound); void onMessage(byte opcode,String data); void onFragment(boolean more,byte opcode,byte[] data, int offset, int length); void onMessage(byte opcode,byte[] data, int offset, int length); - void onDisconnect(); + void onDisconnect(); // TODO add code public interface Outbound { @@ -32,6 +48,7 @@ public interface WebSocket void sendMessage(byte opcode,byte[] data, int offset, int length) throws IOException; void sendFragment(boolean more,byte opcode,byte[] data, int offset, int length) throws IOException; void disconnect(); + void disconnect(int code,String message); boolean isOpen(); } } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java index a05d5e455b2..5f7c0e6b94b 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java @@ -9,7 +9,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Connection; -public interface WebSocketConnection extends Connection, WebSocket.Outbound +public interface WebSocketConnection extends Connection { void fillBuffersFrom(Buffer buffer); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java index 829951bdc94..3dabb8f164f 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java @@ -20,6 +20,7 @@ import java.security.NoSuchAlgorithmException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; @@ -29,13 +30,11 @@ import org.eclipse.jetty.io.nio.IndirectNIOBuffer; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.util.log.Log; -public class WebSocketConnectionD00 implements WebSocketConnection +public class WebSocketConnectionD00 extends AbstractConnection implements WebSocketConnection, WebSocket.Outbound { final IdleCheck _idle; - final EndPoint _endp; final WebSocketParser _parser; final WebSocketGenerator _generator; - final long _timestamp; final WebSocket _websocket; String _key1; String _key2; @@ -50,14 +49,13 @@ public class WebSocketConnectionD00 implements WebSocketConnection public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, int draft) throws IOException { + super(endpoint,timestamp); // TODO - can we use the endpoint idle mechanism? if (endpoint instanceof AsyncEndPoint) ((AsyncEndPoint)endpoint).cancelIdle(); - _endp = endpoint; _endp.setMaxIdleTime(maxIdleTime); - _timestamp = timestamp; _websocket = websocket; // Select the parser/generators to use @@ -220,11 +218,6 @@ public class WebSocketConnectionD00 implements WebSocketConnection _websocket.onDisconnect(); } - public long getTimeStamp() - { - return _timestamp; - } - /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(java.lang.String) @@ -264,12 +257,17 @@ public class WebSocketConnectionD00 implements WebSocketConnection */ public void sendFragment(boolean more,byte opcode, byte[] content, int offset, int length) throws IOException { - _generator.addFragment(more,opcode,content,offset,length,_endp.getMaxIdleTime()); + _generator.addFragment(!more,opcode,content,offset,length,_endp.getMaxIdleTime()); _generator.flush(); checkWriteable(); _idle.access(_endp); } + public void disconnect(int code, String message) + { + throw new UnsupportedOperationException(); + } + public void disconnect() { try @@ -380,4 +378,5 @@ public class WebSocketConnectionD00 implements WebSocketConnection _websocket.onConnect(this); } } + } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD05.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD05.java deleted file mode 100644 index 29da82338ee..00000000000 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD05.java +++ /dev/null @@ -1,373 +0,0 @@ -// ======================================================================== -// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== - -package org.eclipse.jetty.websocket; - -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.jetty.io.AsyncEndPoint; -import org.eclipse.jetty.io.Buffer; -import org.eclipse.jetty.io.ByteArrayBuffer; -import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.nio.IndirectNIOBuffer; -import org.eclipse.jetty.io.nio.SelectChannelEndPoint; -import org.eclipse.jetty.util.log.Log; - -public class WebSocketConnectionD05 implements WebSocketConnection -{ - final IdleCheck _idle; - final EndPoint _endp; - final WebSocketParser _parser; - final WebSocketGenerator _generator; - final long _timestamp; - final WebSocket _websocket; - String _key1; - String _key2; - ByteArrayBuffer _hixieBytes; - - public WebSocketConnectionD05(WebSocket websocket, EndPoint endpoint,int draft) - throws IOException - { - this(websocket,endpoint,new WebSocketBuffers(8192),System.currentTimeMillis(),300000,draft); - } - - public WebSocketConnectionD05(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, int draft) - throws IOException - { - // TODO - can we use the endpoint idle mechanism? - if (endpoint instanceof AsyncEndPoint) - ((AsyncEndPoint)endpoint).cancelIdle(); - - _endp = endpoint; - _endp.setMaxIdleTime(maxIdleTime); - - _timestamp = timestamp; - _websocket = websocket; - _generator = new WebSocketGeneratorD05(buffers, _endp); - _parser = new WebSocketParserD05(buffers, endpoint, new FrameHandlerD5(this,_websocket),true); - - // TODO should these be AsyncEndPoint checks/calls? - if (_endp instanceof SelectChannelEndPoint) - { - final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp; - scep.cancelIdle(); - _idle=new IdleCheck() - { - public void access(EndPoint endp) - { - scep.scheduleIdle(); - } - }; - scep.scheduleIdle(); - } - else - { - _idle = new IdleCheck() - { - public void access(EndPoint endp) - {} - }; - } - } - - public void setHixieKeys(String key1,String key2) - { - _key1=key1; - _key2=key2; - _hixieBytes=new IndirectNIOBuffer(16); - } - - public Connection handle() throws IOException - { - try - { - // handle stupid hixie random bytes - if (_hixieBytes!=null) - { - - // take any available bytes from the parser buffer, which may have already been read - Buffer buffer=_parser.getBuffer(); - if (buffer!=null && buffer.length()>0) - { - int l=buffer.length(); - if (l>(8-_hixieBytes.length())) - l=8-_hixieBytes.length(); - _hixieBytes.put(buffer.peek(buffer.getIndex(),l)); - buffer.skip(l); - } - - // while we are not blocked - while(_endp.isOpen()) - { - // do we now have enough - if (_hixieBytes.length()==8) - { - // we have the silly random bytes - // so let's work out the stupid 16 byte reply. - doTheHixieHixieShake(); - _endp.flush(_hixieBytes); - _hixieBytes=null; - _endp.flush(); - break; - } - - // no, then let's fill - int filled=_endp.fill(_hixieBytes); - if (filled<0) - { - _endp.close(); - break; - } - } - - _websocket.onConnect(this); - return this; - } - - // handle the framing protocol - boolean progress=true; - - while (progress) - { - int flushed=_generator.flush(); - int filled=_parser.parseNext(); - - progress = flushed>0 || filled>0; - - if (filled<0 || flushed<0) - { - _endp.close(); - break; - } - } - } - catch(IOException e) - { - try - { - _endp.close(); - } - catch(IOException e2) - { - Log.ignore(e2); - } - throw e; - } - finally - { - if (_endp.isOpen()) - { - _idle.access(_endp); - checkWriteable(); - } - } - return this; - } - - private void doTheHixieHixieShake() - { - byte[] result=WebSocketConnectionD05.doTheHixieHixieShake( - WebSocketConnectionD05.hixieCrypt(_key1), - WebSocketConnectionD05.hixieCrypt(_key2), - _hixieBytes.asArray()); - _hixieBytes.clear(); - _hixieBytes.put(result); - } - - public boolean isOpen() - { - return _endp!=null&&_endp.isOpen(); - } - - public boolean isIdle() - { - return _parser.isBufferEmpty() && _generator.isBufferEmpty(); - } - - public boolean isSuspended() - { - return false; - } - - public void closed() - { - _websocket.onDisconnect(); - } - - public long getTimeStamp() - { - return _timestamp; - } - - /* ------------------------------------------------------------ */ - /** - * @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(java.lang.String) - */ - public void sendMessage(String content) throws IOException - { - sendMessage(WebSocket.SENTINEL_FRAME,content); - } - - /* ------------------------------------------------------------ */ - /** - * @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(byte, java.lang.String) - */ - public void sendMessage(byte frame, String content) throws IOException - { - _generator.addFrame(frame,content,_endp.getMaxIdleTime()); - _generator.flush(); - checkWriteable(); - _idle.access(_endp); - } - - /* ------------------------------------------------------------ */ - /** - * @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(byte, byte[], int, int) - */ - public void sendMessage(byte opcode, byte[] content, int offset, int length) throws IOException - { - _generator.addFrame(opcode,content,offset,length,_endp.getMaxIdleTime()); - _generator.flush(); - checkWriteable(); - _idle.access(_endp); - } - - /* ------------------------------------------------------------ */ - /** - * @see org.eclipse.jetty.websocket.WebSocketConnection#sendFragment(boolean, byte, byte[], int, int) - */ - public void sendFragment(boolean more,byte opcode, byte[] content, int offset, int length) throws IOException - { - _generator.addFragment(more,opcode,content,offset,length,_endp.getMaxIdleTime()); - _generator.flush(); - checkWriteable(); - _idle.access(_endp); - } - - public void disconnect() - { - try - { - _generator.flush(_endp.getMaxIdleTime()); - _endp.close(); - } - catch(IOException e) - { - Log.ignore(e); - } - } - - public void fillBuffersFrom(Buffer buffer) - { - _parser.fill(buffer); - } - - - private void checkWriteable() - { - if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint) - ((AsyncEndPoint)_endp).scheduleWrite(); - } - - /* ------------------------------------------------------------ */ - static long hixieCrypt(String key) - { - // Don't ask me what all this is about. - // I think it's pretend secret stuff, kind of - // like talking in pig latin! - long number=0; - int spaces=0; - for (char c : key.toCharArray()) - { - if (Character.isDigit(c)) - number=number*10+(c-'0'); - else if (c==' ') - spaces++; - } - return number/spaces; - } - - public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3) - { - try - { - MessageDigest md = MessageDigest.getInstance("MD5"); - byte [] fodder = new byte[16]; - - fodder[0]=(byte)(0xff&(key1>>24)); - fodder[1]=(byte)(0xff&(key1>>16)); - fodder[2]=(byte)(0xff&(key1>>8)); - fodder[3]=(byte)(0xff&key1); - fodder[4]=(byte)(0xff&(key2>>24)); - fodder[5]=(byte)(0xff&(key2>>16)); - fodder[6]=(byte)(0xff&(key2>>8)); - fodder[7]=(byte)(0xff&key2); - for (int i=0;i<8;i++) - fodder[8+i]=key3[i]; - md.update(fodder); - byte[] result=md.digest(); - return result; - } - catch (NoSuchAlgorithmException e) - { - throw new IllegalStateException(e); - } - } - - private interface IdleCheck - { - void access(EndPoint endp); - } - - public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException - { - String uri=request.getRequestURI(); - String query=request.getQueryString(); - if (query!=null && query.length()>0) - uri+="?"+query; - String host=request.getHeader("Host"); - - String key1 = request.getHeader("Sec-WebSocket-Key1"); - if (key1!=null) - { - String key2 = request.getHeader("Sec-WebSocket-Key2"); - setHixieKeys(key1,key2); - - response.setHeader("Upgrade","WebSocket"); - response.addHeader("Connection","Upgrade"); - response.addHeader("Sec-WebSocket-Origin",origin); - response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri); - if (subprotocol!=null) - response.addHeader("Sec-WebSocket-Protocol",subprotocol); - response.sendError(101,"WebSocket Protocol Handshake"); - } - else - { - response.setHeader("Upgrade","WebSocket"); - response.addHeader("Connection","Upgrade"); - response.addHeader("WebSocket-Origin",origin); - response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri); - if (subprotocol!=null) - response.addHeader("WebSocket-Protocol",subprotocol); - response.sendError(101,"Web Socket Protocol Handshake"); - response.flushBuffer(); - _websocket.onConnect(this); - } - } -} diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java new file mode 100644 index 00000000000..43ace22ab09 --- /dev/null +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java @@ -0,0 +1,481 @@ +// ======================================================================== +// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.websocket; + +import java.io.IOException; +import java.security.MessageDigest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AsyncEndPoint; +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.nio.SelectChannelEndPoint; +import org.eclipse.jetty.util.B64Code; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.Utf8StringBuilder; +import org.eclipse.jetty.util.log.Log; + +public class WebSocketConnectionD06 extends AbstractConnection implements WebSocketConnection +{ + private final static byte[] MAGIC="258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(StringUtil.__ISO_8859_1_CHARSET); + private final static byte[] NORMAL_CLOSE=new byte[] { 1000/0xff, (byte)(1000%0xff) }; + private final IdleCheck _idle; + private final WebSocketParser _parser; + private final WebSocketGenerator _generator; + private final WebSocket _websocket; + private boolean _closedIn; + private boolean _closedOut; + + private final WebSocketParser.FrameHandler _frameHandler= new WebSocketParser.FrameHandler() + { + private final Utf8StringBuilder _utf8 = new Utf8StringBuilder(); + private byte _opcode=-1; + + public void onFrame(boolean more, byte flags, byte opcode, Buffer buffer) + { + synchronized(WebSocketConnectionD06.this) + { + // Ignore incoming after a close + if (_closedIn) + return; + + try + { + byte[] array=buffer.array(); + + switch(opcode) + { + case WebSocket.OP_CONTINUATION: + { + // If text, append to the message buffer + if (_opcode==WebSocket.OP_TEXT) + { + _utf8.append(buffer.array(),buffer.getIndex(),buffer.length()); + + // If this is the last fragment, deliver the text buffer + if (!more) + { + String msg =_utf8.toString(); + _utf8.reset(); + _opcode=-1; + _websocket.onMessage(WebSocket.OP_TEXT,msg); + } + } + else + { + // deliver the non-text fragment + if (!more) + _opcode=-1; + _websocket.onFragment(more,_opcode,array,buffer.getIndex(),buffer.length()); + } + break; + } + + case WebSocket.OP_TEXT: + { + if (more) + { + // If this is a text fragment, append to buffer + _opcode=WebSocket.OP_TEXT; + _utf8.append(buffer.array(),buffer.getIndex(),buffer.length()); + } + else + { + // Deliver the message + _websocket.onMessage(opcode,buffer.toString(StringUtil.__UTF8)); + } + break; + } + + case WebSocket.OP_PING: + { + Log.debug("PING {}",this); + if (!_closedOut) + getOutbound().sendMessage(WebSocket.OP_PONG,buffer.array(),buffer.getIndex(),buffer.length()); + break; + } + + case WebSocket.OP_PONG: + { + Log.debug("PONG {}",this); + break; + } + + case WebSocket.OP_CLOSE: + { + int code=-1; + String message=null; + if (buffer.length()>=2) + { + code=buffer.array()[buffer.getIndex()]*0xff+buffer.array()[buffer.getIndex()+1]; + if (buffer.length()>2) + message=new String(buffer.array(),buffer.getIndex()+2,buffer.length()-2,StringUtil.__UTF8_CHARSET); + } + closeIn(code,message); + break; + } + + default: + { + if (more) + { + _opcode=opcode; + _websocket.onFragment(more,opcode,array,buffer.getIndex(),buffer.length()); + } + else + _websocket.onMessage(opcode,array,buffer.getIndex(),buffer.length()); + } + } + } + catch(ThreadDeath th) + { + throw th; + } + catch(Throwable th) + { + Log.warn(th); + } + } + } + + public String toString() + { + return WebSocketConnectionD06.this.toString()+"FH"; + } + }; + + private final WebSocket.Outbound _outbound = new WebSocket.Outbound() + { + volatile boolean _disconnecting; + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(java.lang.String) + */ + public void sendMessage(String content) throws IOException + { + sendMessage(WebSocket.OP_TEXT,content); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(byte, java.lang.String) + */ + public synchronized void sendMessage(byte opcode, String content) throws IOException + { + if (_closedOut) + throw new IOException("closing"); + _generator.addFrame(opcode,content,_endp.getMaxIdleTime()); + _generator.flush(); + checkWriteable(); + _idle.access(_endp); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(byte, byte[], int, int) + */ + public synchronized void sendMessage(byte opcode, byte[] content, int offset, int length) throws IOException + { + if (_closedOut) + throw new IOException("closing"); + _generator.addFrame(opcode,content,offset,length,_endp.getMaxIdleTime()); + _generator.flush(); + checkWriteable(); + _idle.access(_endp); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.websocket.WebSocketConnection#sendFragment(boolean, byte, byte[], int, int) + */ + public void sendFragment(boolean more,byte opcode, byte[] content, int offset, int length) throws IOException + { + if (_closedOut) + throw new IOException("closing"); + _generator.addFragment(!more,opcode,content,offset,length,_endp.getMaxIdleTime()); + _generator.flush(); + checkWriteable(); + _idle.access(_endp); + } + + + /* ------------------------------------------------------------ */ + public boolean isOpen() + { + return _endp!=null&&_endp.isOpen(); + } + + /* ------------------------------------------------------------ */ + public void disconnect(int code, String message) + { + if (_disconnecting) + return; + _disconnecting=true; + WebSocketConnectionD06.this.closeOut(code,message); + } + + /* ------------------------------------------------------------ */ + public void disconnect() + { + if (_disconnecting) + return; + _disconnecting=true; + WebSocketConnectionD06.this.closeOut(1000,null); + } + + }; + + /* ------------------------------------------------------------ */ + public WebSocketConnectionD06(WebSocket websocket, EndPoint endpoint,int draft) + throws IOException + { + this(websocket,endpoint,new WebSocketBuffers(8192),System.currentTimeMillis(),300000,draft); + } + + /* ------------------------------------------------------------ */ + public WebSocketConnectionD06(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, int draft) + throws IOException + { + super(endpoint,timestamp); + + // TODO - can we use the endpoint idle mechanism? + if (endpoint instanceof AsyncEndPoint) + ((AsyncEndPoint)endpoint).cancelIdle(); + + _endp.setMaxIdleTime(maxIdleTime); + + _websocket = websocket; + _generator = new WebSocketGeneratorD06(buffers, _endp,false); + _parser = new WebSocketParserD06(buffers, endpoint, _frameHandler,true); + + // TODO should these be AsyncEndPoint checks/calls? + if (_endp instanceof SelectChannelEndPoint) + { + final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp; + scep.cancelIdle(); + _idle=new IdleCheck() + { + public void access(EndPoint endp) + { + scep.scheduleIdle(); + } + }; + scep.scheduleIdle(); + } + else + { + _idle = new IdleCheck() + { + public void access(EndPoint endp) + {} + }; + } + } + + /* ------------------------------------------------------------ */ + public WebSocket.Outbound getOutbound() + { + return _outbound; + } + + /* ------------------------------------------------------------ */ + public Connection handle() throws IOException + { + try + { + // handle the framing protocol + boolean progress=true; + + while (progress) + { + int flushed=_generator.flush(); + int filled=_parser.parseNext(); + + progress = flushed>0 || filled>0; + + if (filled<0 || flushed<0) + { + _endp.close(); + break; + } + } + } + catch(IOException e) + { + try + { + _endp.close(); + } + catch(IOException e2) + { + Log.ignore(e2); + } + throw e; + } + finally + { + if (_endp.isOpen()) + { + _idle.access(_endp); + if (_closedIn && _closedOut && _generator.isBufferEmpty()) + _endp.close(); + else + checkWriteable(); + } + + } + return this; + } + + /* ------------------------------------------------------------ */ + public boolean isIdle() + { + return _parser.isBufferEmpty() && _generator.isBufferEmpty(); + } + + /* ------------------------------------------------------------ */ + @Override + public void idleExpired() + { + closeOut(WebSocket.CLOSE_NORMAL,"Idle"); + } + + /* ------------------------------------------------------------ */ + public boolean isSuspended() + { + return false; + } + + /* ------------------------------------------------------------ */ + public void closed() + { + _websocket.onDisconnect(); + } + + /* ------------------------------------------------------------ */ + public synchronized void closeIn(int code,String message) + { + Log.debug("ClosedIn {} {}",this,message); + try + { + if (_closedOut) + _endp.close(); + else + closeOut(code,message); + } + catch(IOException e) + { + Log.ignore(e); + } + finally + { + _closedIn=true; + } + } + + /* ------------------------------------------------------------ */ + public synchronized void closeOut(int code,String message) + { + Log.debug("ClosedOut {} {}",this,message); + try + { + if (_closedIn || _closedOut) + _endp.close(); + else if (code<=0) + { + _generator.addFrame(WebSocket.OP_CLOSE,NORMAL_CLOSE,0,2,_endp.getMaxIdleTime()); + } + else + { + byte[] bytes = ("xx"+(message==null?"":message)).getBytes(StringUtil.__ISO_8859_1_CHARSET); + bytes[0]=(byte)(code/0xff); + bytes[1]=(byte)(code%0xff); + _generator.addFrame(WebSocket.OP_CLOSE,bytes,0,bytes.length,_endp.getMaxIdleTime()); + } + _generator.flush(); + + } + catch(IOException e) + { + Log.ignore(e); + } + finally + { + _closedOut=true; + } + } + + + /* ------------------------------------------------------------ */ + public void fillBuffersFrom(Buffer buffer) + { + _parser.fill(buffer); + } + + + /* ------------------------------------------------------------ */ + private void checkWriteable() + { + if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint) + ((AsyncEndPoint)_endp).scheduleWrite(); + } + + /* ------------------------------------------------------------ */ + private interface IdleCheck + { + void access(EndPoint endp); + } + + /* ------------------------------------------------------------ */ + public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException + { + String uri=request.getRequestURI(); + String query=request.getQueryString(); + if (query!=null && query.length()>0) + uri+="?"+query; + String key = request.getHeader("Sec-WebSocket-Key"); + + response.setHeader("Upgrade","WebSocket"); + response.addHeader("Connection","Upgrade"); + response.addHeader("Sec-WebSocket-Accept",hashKey(key)); + if (subprotocol!=null) + response.addHeader("Sec-WebSocket-Protocol",subprotocol); + response.sendError(101); + + _websocket.onConnect(_outbound); + } + + /* ------------------------------------------------------------ */ + public static String hashKey(String key) + { + try + { + MessageDigest md = MessageDigest.getInstance("SHA1"); + md.update(key.getBytes("UTF-8")); + md.update(MAGIC); + return new String(B64Code.encode(md.digest())); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + +} diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java index e24ec0359a4..9adf5ee7cf2 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java @@ -93,23 +93,30 @@ public class WebSocketFactory * @throws IOException */ public void upgrade(HttpServletRequest request,HttpServletResponse response, WebSocket websocket, String origin, String subprotocol) - throws IOException + throws IOException { - if (!"WebSocket".equals(request.getHeader("Upgrade"))) + if (!"websocket".equalsIgnoreCase(request.getHeader("Upgrade"))) throw new IllegalStateException("!Upgrade:websocket"); if (!"HTTP/1.1".equals(request.getProtocol())) throw new IllegalStateException("!HTTP/1.1"); - int draft=request.getIntHeader("Sec-WebSocket-Draft"); + int draft=request.getIntHeader("Sec-WebSocket-Version"); + if (draft<0) + draft=request.getIntHeader("Sec-WebSocket-Draft"); HttpConnection http = HttpConnection.getCurrentConnection(); ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint(); final WebSocketConnection connection; switch(draft) { - case 5: - connection=new WebSocketConnectionD05(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft); + case 6: + connection=new WebSocketConnectionD06(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft); break; + case 5: + case 4: + case 3: + case 2: + throw new UnsupportedOperationException("Unsupported draft specification: "+draft); default: connection=new WebSocketConnectionD00(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft); } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGenerator.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGenerator.java index b5b46d08902..b889e464205 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGenerator.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGenerator.java @@ -26,6 +26,6 @@ public interface WebSocketGenerator boolean isBufferEmpty(); void addFrame(byte opcode, String content, int maxIdleTime) throws IOException; void addFrame(byte opcode, byte[] content, int offset, int length, int maxIdleTime) throws IOException; - void addFragment(boolean more,byte opcode, byte[] content, int offset, int length, int maxIdleTime) throws IOException; + void addFragment(boolean last, byte opcode,byte[] content, int offset, int length, int maxIdleTime) throws IOException; int flush(int maxIdleTime) throws IOException; } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD00.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD00.java index 3744102423b..5fa06f42ca3 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD00.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD00.java @@ -171,9 +171,9 @@ public class WebSocketGeneratorD00 implements WebSocketGenerator return _buffer==null || _buffer.length()==0; } - public void addFragment(boolean more, byte opcode, byte[] content, int offset, int length, int maxIdleTime) throws IOException + public void addFragment(boolean last,byte opcode, byte[] content, int offset, int length, int maxIdleTime) throws IOException { - if (more) + if (!last) throw new UnsupportedOperationException("fragmented"); addFrame(opcode,content,offset,length,maxIdleTime); } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD01.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD01.java index e4f699c4bc2..e6b5d80cdc1 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD01.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD01.java @@ -47,10 +47,10 @@ public class WebSocketGeneratorD01 implements WebSocketGenerator public synchronized void addFrame(byte opcode,byte[] content, int offset, int length, int blockFor) throws IOException { - addFragment(false,opcode,content,offset,length,blockFor); + addFragment(true,opcode,content,offset,length,blockFor); } - public synchronized void addFragment(boolean more, byte opcode, byte[] content, int offset, int length, int blockFor) throws IOException + public synchronized void addFragment(boolean last,byte opcode, byte[] content, int offset, int length, int blockFor) throws IOException { if (_buffer==null) _buffer=_buffers.getDirectBuffer(); @@ -69,10 +69,10 @@ public class WebSocketGeneratorD01 implements WebSocketGenerator fragment=_buffer.capacity()-10; bufferPut((byte)(0x80|opcode), blockFor); } - else if (more) - bufferPut((byte)(0x80|opcode), blockFor); - else + else if (last) bufferPut(opcode, blockFor); + else + bufferPut((byte)(0x80|opcode), blockFor); if (fragment>0xffff) { diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD05.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06.java similarity index 58% rename from jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD05.java rename to jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06.java index a07afc9ecf8..0ebddcef7d9 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD05.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06.java @@ -14,6 +14,8 @@ package org.eclipse.jetty.websocket; import java.io.IOException; +import java.security.SecureRandom; +import java.util.Random; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.EndPoint; @@ -27,82 +29,112 @@ import org.eclipse.jetty.io.EofException; * threads will call the addMessage methods while other * threads are flushing the generator. */ -public class WebSocketGeneratorD05 implements WebSocketGenerator +public class WebSocketGeneratorD06 implements WebSocketGenerator { final private WebSocketBuffers _buffers; final private EndPoint _endp; private Buffer _buffer; + private final boolean _masked; + private final byte[] _mask=new byte[4]; + private final Random _random; + private int _m; + private boolean _opsent; - public WebSocketGeneratorD05(WebSocketBuffers buffers, EndPoint endp) + public WebSocketGeneratorD06(WebSocketBuffers buffers, EndPoint endp, boolean masked) { _buffers=buffers; _endp=endp; + _masked=masked; + _random=_masked?new SecureRandom():null; // TODO share the Random } public synchronized void addFrame(byte opcode,byte[] content, int blockFor) throws IOException { + _opsent=false; addFrame(opcode,content,0,content.length,blockFor); } public synchronized void addFrame(byte opcode,byte[] content, int offset, int length, int blockFor) throws IOException { - addFragment(false,opcode,content,offset,length,blockFor); + _opsent=false; + addFragment(true,opcode,content,offset,length,blockFor); } - public synchronized void addFragment(boolean more, byte opcode, byte[] content, int offset, int length, int blockFor) throws IOException + public synchronized void addFragment(boolean last, byte opcode, byte[] content, int offset, int length, int blockFor) throws IOException { if (_buffer==null) - _buffer=_buffers.getDirectBuffer(); - - if (_buffer.space() == 0) - expelBuffer(blockFor); + _buffer=_masked?_buffers.getBuffer():_buffers.getDirectBuffer(); + + int space=_masked?14:10; - opcode = (byte)(opcode & 0x0f); - - while (length>0) + do { - // slice a fragment off - int fragment=length; - if (fragment+10>_buffer.capacity()) - { - fragment=_buffer.capacity()-10; - bufferPut((byte)(0x80|opcode), blockFor); - } - else if (more) - bufferPut((byte)(0x80|opcode), blockFor); - else - bufferPut(opcode, blockFor); + opcode = _opsent?WebSocket.OP_CONTINUATION:(byte)(opcode & 0x0f); + _opsent=true; + + int payload=length; + if (payload+space>_buffer.capacity()) + payload=_buffer.capacity()-space; + else if (last) + opcode|=(byte)0x80; // Set the FIN bit - if (fragment>0xffff) + // ensure there is space for header + if (_buffer.space() <= space) + expelBuffer(blockFor); + + // write mask + if (_masked) { - bufferPut((byte)0x7f, blockFor); - bufferPut((byte)((fragment>>56)&0x7f), blockFor); - bufferPut((byte)((fragment>>48)&0xff), blockFor); - bufferPut((byte)((fragment>>40)&0xff), blockFor); - bufferPut((byte)((fragment>>32)&0xff), blockFor); - bufferPut((byte)((fragment>>24)&0xff), blockFor); - bufferPut((byte)((fragment>>16)&0xff), blockFor); - bufferPut((byte)((fragment>>8)&0xff), blockFor); - bufferPut((byte)(fragment&0xff), blockFor); + _random.nextBytes(_mask); + _m=0; + _buffer.put(_mask); } - else if (fragment >=0x7e) + + // write the opcode and length + if (payload>0xffff) { - bufferPut((byte)126, blockFor); - bufferPut((byte)(fragment>>8), blockFor); - bufferPut((byte)(fragment&0xff), blockFor); + bufferPut(new byte[]{ + opcode, + (byte)0x7f, + (byte)((payload>>56)&0x7f), + (byte)((payload>>48)&0xff), + (byte)((payload>>40)&0xff), + (byte)((payload>>32)&0xff), + (byte)((payload>>24)&0xff), + (byte)((payload>>16)&0xff), + (byte)((payload>>8)&0xff), + (byte)(payload&0xff)}); + } + else if (payload >=0x7e) + { + bufferPut(new byte[]{ + opcode, + (byte)0x7e, + (byte)(payload>>8), + (byte)(payload&0xff)}); } else { - bufferPut((byte)fragment, blockFor); + bufferPut(opcode); + bufferPut((byte)payload); } - int remaining = fragment; + // write payload + int remaining = payload; while (remaining > 0) { _buffer.compact(); int chunk = remaining < _buffer.space() ? remaining : _buffer.space(); - _buffer.put(content, offset + (fragment - remaining), chunk); + + if (_masked) + { + for (int i=0;i 0) { @@ -120,18 +152,24 @@ public class WebSocketGeneratorD05 implements WebSocketGenerator } } } - offset+=fragment; - length-=fragment; + offset+=payload; + length-=payload; } + while (length>0); + _opsent=!last; } - private synchronized void bufferPut(byte datum, long blockFor) throws IOException + private synchronized void bufferPut(byte[] data) throws IOException { - if (_buffer==null) - _buffer=_buffers.getDirectBuffer(); - _buffer.put(datum); - if (_buffer.space() == 0) - expelBuffer(blockFor); + if (_masked) + for (int i=0;i>4); - _more=(_flags&8)!=0; + _fin=(_flags&8)!=0; _state=State.LENGTH_7; continue; @@ -226,7 +226,7 @@ public class WebSocketParserD05 implements WebSocketParser array[data.getIndex()+i]^=_mask[_m++%4]; } - _handler.onFrame(_more,_flags, _opcode, data); + _handler.onFrame(!_fin,_flags, _opcode, data); _count=0; _state=State.OPCODE; diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketServlet.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketServlet.java index 6902aaf0f01..fed192700e2 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketServlet.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketServlet.java @@ -64,9 +64,9 @@ public abstract class WebSocketServlet extends HttpServlet { boolean hixie = request.getHeader("Sec-WebSocket-Key1")!=null; - String protocol=request.getHeader(hixie?"Sec-WebSocket-Protocol":"WebSocket-Protocol"); - if (protocol==null) - protocol=request.getHeader("Sec-WebSocket-Protocol"); + String protocol=request.getHeader("Sec-WebSocket-Protocol"); + if (protocol==null) // TODO remove once draft period is over + protocol=request.getHeader("WebSocket-Protocol"); WebSocket websocket=doWebSocketConnect(request,protocol); String host=request.getHeader("Host"); diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06Test.java new file mode 100644 index 00000000000..ac524758e36 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06Test.java @@ -0,0 +1,198 @@ +package org.eclipse.jetty.websocket; + +import static junit.framework.Assert.assertEquals; + +import org.eclipse.jetty.io.ByteArrayBuffer; +import org.eclipse.jetty.io.ByteArrayEndPoint; +import org.eclipse.jetty.util.StringUtil; +import org.junit.Before; +import org.junit.Test; + +/** + * @version $Revision$ $Date$ + */ +public class WebSocketGeneratorD06Test +{ + private ByteArrayBuffer _out; + private WebSocketGenerator _generator; + ByteArrayEndPoint _endPoint; + WebSocketBuffers _buffers; + byte[] _mask = new byte[4]; + int _m; + + + @Before + public void setUp() throws Exception + { + _endPoint = new ByteArrayEndPoint(); + _out = new ByteArrayBuffer(2048); + _endPoint.setOut(_out); + _buffers = new WebSocketBuffers(1024); + _m=0; + } + + byte getMasked() + { + return (byte)(_out.get()^_mask[_m++%4]); + } + + + @Test + public void testOneString() throws Exception + { + _generator = new WebSocketGeneratorD06(_buffers, _endPoint,false); + + + _generator.addFrame((byte)0x04,"Hell\uFF4F W\uFF4Frld",0); + _generator.flush(); + assertEquals((byte)0x84,_out.get()); + assertEquals(15,0xff&_out.get()); + assertEquals('H',_out.get()); + assertEquals('e',_out.get()); + assertEquals('l',_out.get()); + assertEquals('l',_out.get()); + assertEquals(0xEF,0xff&_out.get()); + assertEquals(0xBD,0xff&_out.get()); + assertEquals(0x8F,0xff&_out.get()); + assertEquals(' ',_out.get()); + assertEquals('W',_out.get()); + assertEquals(0xEF,0xff&_out.get()); + assertEquals(0xBD,0xff&_out.get()); + assertEquals(0x8F,0xff&_out.get()); + assertEquals('r',_out.get()); + assertEquals('l',_out.get()); + assertEquals('d',_out.get()); + } + + @Test + public void testOneBuffer() throws Exception + { + _generator = new WebSocketGeneratorD06(_buffers, _endPoint,false); + + String string = "Hell\uFF4F W\uFF4Frld"; + byte[] bytes=string.getBytes(StringUtil.__UTF8); + _generator.addFrame((byte)0x04,bytes,0,bytes.length,0); + _generator.flush(); + assertEquals((byte)0x84,_out.get()); + assertEquals(15,0xff&_out.get()); + assertEquals('H',_out.get()); + assertEquals('e',_out.get()); + assertEquals('l',_out.get()); + assertEquals('l',_out.get()); + assertEquals(0xEF,0xff&_out.get()); + assertEquals(0xBD,0xff&_out.get()); + assertEquals(0x8F,0xff&_out.get()); + assertEquals(' ',_out.get()); + assertEquals('W',_out.get()); + assertEquals(0xEF,0xff&_out.get()); + assertEquals(0xBD,0xff&_out.get()); + assertEquals(0x8F,0xff&_out.get()); + assertEquals('r',_out.get()); + assertEquals('l',_out.get()); + assertEquals('d',_out.get()); + } + + @Test + public void testOneLongBuffer() throws Exception + { + _generator = new WebSocketGeneratorD06(_buffers, _endPoint,false); + + byte[] b=new byte[150]; + for (int i=0;i>8)); _in.put((byte)(bytes.length&0xff)); @@ -164,7 +164,7 @@ public class WebSocketParserD05Test WebSocketBuffers buffers = new WebSocketBuffers(0x20000); ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); - WebSocketParser parser=new WebSocketParserD01(buffers, endPoint,_handler); + WebSocketParser parser=new WebSocketParserD06(buffers, endPoint,_handler,false); ByteArrayBuffer in = new ByteArrayBuffer(0x20000); endPoint.setIn(in); @@ -176,7 +176,7 @@ public class WebSocketParserD05Test byte[] bytes = string.getBytes("UTF-8"); - in.put((byte)0x00); + in.put((byte)0x84); in.put((byte)0x7F); in.put((byte)0x00); in.put((byte)0x00); @@ -199,10 +199,10 @@ public class WebSocketParserD05Test @Test public void testShortFragmentTest() throws Exception { - _in.put((byte)0x80); + _in.put((byte)0x04); _in.put((byte)0x06); _in.put("Hello ".getBytes(StringUtil.__UTF8)); - _in.put((byte)0x00); + _in.put((byte)0x80); _in.put((byte)0x05); _in.put("World".getBytes(StringUtil.__UTF8)); @@ -232,11 +232,11 @@ public class WebSocketParserD05Test if (more) _utf8.append(buffer.array(),buffer.getIndex(),buffer.length()); else if (_utf8.length()==0) - _data.add(opcode,buffer.toString("utf-8")); + _data.add(buffer.toString("utf-8")); else { _utf8.append(buffer.array(),buffer.getIndex(),buffer.length()); - _data.add(opcode,_utf8.toString()); + _data.add(_utf8.toString()); _utf8.reset(); } }