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();
}
}