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
This commit is contained in:
parent
8020110283
commit
2cc4fbb5ee
|
@ -11,7 +11,7 @@ jetty-7.3.1-SNAPSHOT
|
||||||
+ 337270 Shared Timer for session management
|
+ 337270 Shared Timer for session management
|
||||||
+ 337271 Flush SSL endpoint when dispatch thread held forever
|
+ 337271 Flush SSL endpoint when dispatch thread held forever
|
||||||
+ 337678 Readded optional async connection mode for HttpClient
|
+ 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
|
+ 337746 Fixed Session deIdle recursion
|
||||||
+ 337784 Improve HashSessionManager for session migrations
|
+ 337784 Improve HashSessionManager for session migrations
|
||||||
+ 337878 Extra tests of security constraints
|
+ 337878 Extra tests of security constraints
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.http.HttpParser;
|
||||||
import org.eclipse.jetty.http.HttpSchemes;
|
import org.eclipse.jetty.http.HttpSchemes;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.HttpVersions;
|
import org.eclipse.jetty.http.HttpVersions;
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
import org.eclipse.jetty.io.Buffers;
|
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) $
|
* @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 HttpDestination _destination;
|
||||||
private EndPoint _endp;
|
|
||||||
private HttpGenerator _generator;
|
private HttpGenerator _generator;
|
||||||
private HttpParser _parser;
|
private HttpParser _parser;
|
||||||
private boolean _http11 = true;
|
private boolean _http11 = true;
|
||||||
|
@ -56,6 +56,7 @@ public class HttpConnection implements Connection
|
||||||
private Buffer _requestContentChunk;
|
private Buffer _requestContentChunk;
|
||||||
private boolean _requestComplete;
|
private boolean _requestComplete;
|
||||||
private boolean _reserved;
|
private boolean _reserved;
|
||||||
|
|
||||||
// The current exchange waiting for a response
|
// The current exchange waiting for a response
|
||||||
private volatile HttpExchange _exchange;
|
private volatile HttpExchange _exchange;
|
||||||
private HttpExchange _pipeline;
|
private HttpExchange _pipeline;
|
||||||
|
@ -64,6 +65,7 @@ public class HttpConnection implements Connection
|
||||||
|
|
||||||
public void dump() throws IOException
|
public void dump() throws IOException
|
||||||
{
|
{
|
||||||
|
// TODO update to dumpable
|
||||||
Log.info("endp=" + _endp + " " + _endp.isBufferingInput() + " " + _endp.isBufferingOutput());
|
Log.info("endp=" + _endp + " " + _endp.isBufferingInput() + " " + _endp.isBufferingOutput());
|
||||||
Log.info("generator=" + _generator);
|
Log.info("generator=" + _generator);
|
||||||
Log.info("parser=" + _parser.getState() + " " + _parser.isMoreInBuffer());
|
Log.info("parser=" + _parser.getState() + " " + _parser.isMoreInBuffer());
|
||||||
|
@ -74,16 +76,11 @@ public class HttpConnection implements Connection
|
||||||
|
|
||||||
HttpConnection(Buffers requestBuffers, Buffers responseBuffers, EndPoint endp)
|
HttpConnection(Buffers requestBuffers, Buffers responseBuffers, EndPoint endp)
|
||||||
{
|
{
|
||||||
_endp = endp;
|
super(endp);
|
||||||
_generator = new HttpGenerator(requestBuffers,endp);
|
_generator = new HttpGenerator(requestBuffers,endp);
|
||||||
_parser = new HttpParser(responseBuffers,endp,new Handler());
|
_parser = new HttpParser(responseBuffers,endp,new Handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeStamp()
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReserved (boolean reserved)
|
public void setReserved (boolean reserved)
|
||||||
{
|
{
|
||||||
_reserved = reserved;
|
_reserved = reserved;
|
||||||
|
@ -442,11 +439,6 @@ public class HttpConnection implements Connection
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public EndPoint getEndPoint()
|
|
||||||
{
|
|
||||||
return _endp;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void commitRequest() throws IOException
|
private void commitRequest() throws IOException
|
||||||
{
|
{
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.eclipse.jetty.http.HttpHeaders;
|
||||||
import org.eclipse.jetty.http.HttpMethods;
|
import org.eclipse.jetty.http.HttpMethods;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.PathMap;
|
import org.eclipse.jetty.http.PathMap;
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
import org.eclipse.jetty.io.ByteArrayBuffer;
|
import org.eclipse.jetty.io.ByteArrayBuffer;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
@ -312,7 +313,7 @@ public class HttpDestination
|
||||||
|
|
||||||
public void onNewConnection(final HttpConnection connection) throws IOException
|
public void onNewConnection(final HttpConnection connection) throws IOException
|
||||||
{
|
{
|
||||||
HttpConnection q_connection = null;
|
AbstractConnection q_connection = null;
|
||||||
|
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.jetty.http.HttpURI;
|
||||||
import org.eclipse.jetty.http.HttpVersions;
|
import org.eclipse.jetty.http.HttpVersions;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
import org.eclipse.jetty.io.BufferCache.CachedBuffer;
|
import org.eclipse.jetty.io.BufferCache.CachedBuffer;
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.ByteArrayBuffer;
|
import org.eclipse.jetty.io.ByteArrayBuffer;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
@ -690,9 +691,9 @@ public class HttpExchange
|
||||||
return this._connection != null;
|
return this._connection != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpConnection disassociate()
|
AbstractConnection disassociate()
|
||||||
{
|
{
|
||||||
HttpConnection result = _connection;
|
AbstractConnection result = _connection;
|
||||||
this._connection = null;
|
this._connection = null;
|
||||||
if (getStatus() == STATUS_CANCELLING)
|
if (getStatus() == STATUS_CANCELLING)
|
||||||
setStatus(STATUS_CANCELLED);
|
setStatus(STATUS_CANCELLED);
|
||||||
|
|
|
@ -30,6 +30,7 @@ import junit.framework.TestCase;
|
||||||
import org.eclipse.jetty.client.security.ProxyAuthorization;
|
import org.eclipse.jetty.client.security.ProxyAuthorization;
|
||||||
import org.eclipse.jetty.http.HttpHeaders;
|
import org.eclipse.jetty.http.HttpHeaders;
|
||||||
import org.eclipse.jetty.http.HttpMethods;
|
import org.eclipse.jetty.http.HttpMethods;
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
import org.eclipse.jetty.io.ByteArrayBuffer;
|
import org.eclipse.jetty.io.ByteArrayBuffer;
|
||||||
import org.eclipse.jetty.io.EofException;
|
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
|
//try to get a connection, and only wait 500ms, as we have
|
||||||
//already reserved the max, should return null
|
//already reserved the max, should return null
|
||||||
org.eclipse.jetty.client.HttpConnection c = destination.reserveConnection(500);
|
AbstractConnection c = destination.reserveConnection(500);
|
||||||
assertNull(c);
|
assertNull(c);
|
||||||
|
|
||||||
//unreserve first connection
|
//unreserve first connection
|
||||||
|
|
|
@ -636,6 +636,7 @@ public abstract class AbstractBuffer implements Buffer
|
||||||
if (bytes!=null)
|
if (bytes!=null)
|
||||||
return new String(bytes,getIndex(),length(),charset);
|
return new String(bytes,getIndex(),length(),charset);
|
||||||
return new String(asArray(), 0, length(),charset);
|
return new String(asArray(), 0, length(),charset);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,10 +38,21 @@ public interface Connection
|
||||||
*/
|
*/
|
||||||
Connection handle() throws IOException;
|
Connection handle() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the timestamp at which the connection was created
|
||||||
|
*/
|
||||||
long getTimeStamp();
|
long getTimeStamp();
|
||||||
|
|
||||||
boolean isIdle();
|
boolean isIdle();
|
||||||
boolean isSuspended();
|
boolean isSuspended();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the connection idle timeout expires
|
||||||
|
*/
|
||||||
|
void idleExpired();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the connection is closed
|
||||||
|
*/
|
||||||
void closed();
|
void closed();
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,14 +248,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
protected void idleExpired()
|
protected void idleExpired()
|
||||||
{
|
{
|
||||||
try
|
_connection.idleExpired();
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
Log.ignore(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.eclipse.jetty.http.HttpURI;
|
||||||
import org.eclipse.jetty.http.HttpVersions;
|
import org.eclipse.jetty.http.HttpVersions;
|
||||||
import org.eclipse.jetty.http.MimeTypes;
|
import org.eclipse.jetty.http.MimeTypes;
|
||||||
import org.eclipse.jetty.http.Parser;
|
import org.eclipse.jetty.http.Parser;
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
import org.eclipse.jetty.io.BufferCache.CachedBuffer;
|
import org.eclipse.jetty.io.BufferCache.CachedBuffer;
|
||||||
|
@ -86,17 +87,15 @@ import org.eclipse.jetty.util.thread.Timeout;
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class HttpConnection implements Connection
|
public class HttpConnection extends AbstractConnection implements Connection
|
||||||
{
|
{
|
||||||
private static final int UNKNOWN = -2;
|
private static final int UNKNOWN = -2;
|
||||||
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<HttpConnection>();
|
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<HttpConnection>();
|
||||||
|
|
||||||
private final long _timeStamp=System.currentTimeMillis();
|
|
||||||
private int _requests;
|
private int _requests;
|
||||||
private volatile boolean _handling;
|
private volatile boolean _handling;
|
||||||
|
|
||||||
protected final Connector _connector;
|
protected final Connector _connector;
|
||||||
protected final EndPoint _endp;
|
|
||||||
protected final Server _server;
|
protected final Server _server;
|
||||||
protected final HttpURI _uri;
|
protected final HttpURI _uri;
|
||||||
|
|
||||||
|
@ -143,9 +142,9 @@ public class HttpConnection implements Connection
|
||||||
*/
|
*/
|
||||||
public HttpConnection(Connector connector, EndPoint endpoint, Server server)
|
public HttpConnection(Connector connector, EndPoint endpoint, Server server)
|
||||||
{
|
{
|
||||||
|
super(endpoint);
|
||||||
_uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
|
_uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
|
||||||
_connector = connector;
|
_connector = connector;
|
||||||
_endp = endpoint;
|
|
||||||
HttpBuffers ab = (HttpBuffers)_connector;
|
HttpBuffers ab = (HttpBuffers)_connector;
|
||||||
_parser = new HttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler());
|
_parser = new HttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler());
|
||||||
_requestFields = new HttpFields();
|
_requestFields = new HttpFields();
|
||||||
|
@ -161,9 +160,9 @@ public class HttpConnection implements Connection
|
||||||
protected HttpConnection(Connector connector, EndPoint endpoint, Server server,
|
protected HttpConnection(Connector connector, EndPoint endpoint, Server server,
|
||||||
Parser parser, Generator generator, Request request)
|
Parser parser, Generator generator, Request request)
|
||||||
{
|
{
|
||||||
|
super(endpoint);
|
||||||
_uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
|
_uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
|
||||||
_connector = connector;
|
_connector = connector;
|
||||||
_endp = endpoint;
|
|
||||||
_parser = parser;
|
_parser = parser;
|
||||||
_requestFields = new HttpFields();
|
_requestFields = new HttpFields();
|
||||||
_responseFields = new HttpFields(server.getMaxCookieVersion());
|
_responseFields = new HttpFields(server.getMaxCookieVersion());
|
||||||
|
@ -198,15 +197,6 @@ public class HttpConnection implements Connection
|
||||||
return _server;
|
return _server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The time this connection was established.
|
|
||||||
*/
|
|
||||||
public long getTimeStamp()
|
|
||||||
{
|
|
||||||
return _timeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @return Returns the associatedObject.
|
* @return Returns the associatedObject.
|
||||||
|
@ -278,15 +268,6 @@ public class HttpConnection implements Connection
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The {@link EndPoint} for this connection.
|
|
||||||
*/
|
|
||||||
public EndPoint getEndPoint()
|
|
||||||
{
|
|
||||||
return _endp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @return <code>false</code> (this method is not yet implemented)
|
* @return <code>false</code> (this method is not yet implemented)
|
||||||
|
|
|
@ -15,6 +15,7 @@ import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.http.HttpMethods;
|
import org.eclipse.jetty.http.HttpMethods;
|
||||||
import org.eclipse.jetty.http.HttpParser;
|
import org.eclipse.jetty.http.HttpParser;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
|
@ -279,10 +280,9 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
private ClientToProxyConnection prepareConnections(ConcurrentMap<String, Object> context, SocketChannel channel, Buffer buffer)
|
private ClientToProxyConnection prepareConnections(ConcurrentMap<String, Object> context, SocketChannel channel, Buffer buffer)
|
||||||
{
|
{
|
||||||
HttpConnection httpConnection = HttpConnection.getCurrentConnection();
|
HttpConnection httpConnection = HttpConnection.getCurrentConnection();
|
||||||
ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer);
|
|
||||||
ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
|
ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
|
||||||
|
ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer,clientToProxy);
|
||||||
clientToProxy.setConnection(proxyToServer);
|
clientToProxy.setConnection(proxyToServer);
|
||||||
proxyToServer.setConnection(clientToProxy);
|
|
||||||
return clientToProxy;
|
return clientToProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,9 +307,9 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
return new ClientToProxyConnection(context, channel, endPoint, timeStamp);
|
return new ClientToProxyConnection(context, channel, endPoint, timeStamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer buffer)
|
protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap<String, Object> 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
|
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
|
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey selectionKey) throws IOException
|
||||||
{
|
{
|
||||||
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, selectionKey);
|
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, selectionKey);
|
||||||
// TODO endp.setMaxIdleTime(_writeTimeout);
|
endp.setMaxIdleTime(_writeTimeout);
|
||||||
return endp;
|
return endp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,8 +421,7 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint)
|
protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint)
|
||||||
{
|
{
|
||||||
ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
|
ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
|
||||||
proxyToServer.setTimeStamp(System.currentTimeMillis());
|
proxyToServer = new ProxyToServerConnection(endpoint,proxyToServer);
|
||||||
proxyToServer.setEndPoint(endpoint);
|
|
||||||
return 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 CountDownLatch _ready ;
|
||||||
private final Buffer _buffer = new IndirectNIOBuffer(1024);
|
private final Buffer _buffer;
|
||||||
private final ConcurrentMap<String, Object> _context;
|
private final ConcurrentMap<String, Object> _context;
|
||||||
|
private final ClientToProxyConnection _toClient;
|
||||||
private volatile Buffer _data;
|
private volatile Buffer _data;
|
||||||
private volatile ClientToProxyConnection _toClient;
|
|
||||||
private volatile long _timestamp;
|
|
||||||
private volatile SelectChannelEndPoint _endPoint;
|
|
||||||
|
|
||||||
public ProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer data)
|
public ProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer data, ClientToProxyConnection toClient)
|
||||||
{
|
{
|
||||||
|
super(null);
|
||||||
_context = context;
|
_context = context;
|
||||||
_data = data;
|
_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
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
StringBuilder builder = new StringBuilder("ProxyToServer");
|
StringBuilder builder = new StringBuilder("ProxyToServer");
|
||||||
builder.append("(:").append(_endPoint.getLocalPort());
|
builder.append("(:").append(_endp.getLocalPort());
|
||||||
builder.append("<=>:").append(_endPoint.getRemotePort());
|
builder.append("<=>:").append(_endp.getRemotePort());
|
||||||
return builder.append(")").toString();
|
return builder.append(")").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,18 +496,18 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
{
|
{
|
||||||
if (_data != null)
|
if (_data != null)
|
||||||
{
|
{
|
||||||
int written = write(_endPoint, _data, _context);
|
int written = write(_endp, _data, _context);
|
||||||
_logger.debug("{}: written to server {} bytes", this, written);
|
_logger.debug("{}: written to server {} bytes", this, written);
|
||||||
_data = null;
|
_data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int read = read(_endPoint, _buffer, _context);
|
int read = read(_endp, _buffer, _context);
|
||||||
|
|
||||||
if (read == -1)
|
if (read == -1)
|
||||||
{
|
{
|
||||||
_logger.debug("{}: server closed connection {}", this, _endPoint);
|
_logger.debug("{}: server closed connection {}", this, _endp);
|
||||||
close();
|
close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -501,8 +515,8 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
if (read == 0)
|
if (read == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
_logger.debug("{}: read from server {} bytes {}", this, read, _endPoint);
|
_logger.debug("{}: read from server {} bytes {}", this, read, _endp);
|
||||||
int written = write(_toClient._endPoint, _buffer, _context);
|
int written = write(_toClient.getEndPoint(), _buffer, _context);
|
||||||
_logger.debug("{}: written to {} {} bytes", this, _toClient, written);
|
_logger.debug("{}: written to {} {} bytes", this, _toClient, written);
|
||||||
}
|
}
|
||||||
return this;
|
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()
|
public boolean isIdle()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -588,7 +582,7 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
|
|
||||||
public void closeServer() throws IOException
|
public void closeServer() throws IOException
|
||||||
{
|
{
|
||||||
_endPoint.close();
|
_endp.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void 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 Buffer _buffer = new IndirectNIOBuffer(1024);
|
||||||
private final ConcurrentMap<String, Object> _context;
|
private final ConcurrentMap<String, Object> _context;
|
||||||
private final SocketChannel _channel;
|
private final SocketChannel _channel;
|
||||||
private final EndPoint _endPoint;
|
|
||||||
private final long _timestamp;
|
|
||||||
private volatile ProxyToServerConnection _toServer;
|
private volatile ProxyToServerConnection _toServer;
|
||||||
private boolean _firstTime = true;
|
private boolean _firstTime = true;
|
||||||
|
|
||||||
public ClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timestamp)
|
public ClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timestamp)
|
||||||
{
|
{
|
||||||
|
super(endPoint,timestamp);
|
||||||
_context = context;
|
_context = context;
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
_endPoint = endPoint;
|
|
||||||
_timestamp = timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
StringBuilder builder = new StringBuilder("ClientToProxy");
|
StringBuilder builder = new StringBuilder("ClientToProxy");
|
||||||
builder.append("(:").append(_endPoint.getLocalPort());
|
builder.append("(:").append(_endp.getLocalPort());
|
||||||
builder.append("<=>:").append(_endPoint.getRemotePort());
|
builder.append("<=>:").append(_endp.getRemotePort());
|
||||||
return builder.append(")").toString();
|
return builder.append(")").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,11 +645,11 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int read = read(_endPoint, _buffer, _context);
|
int read = read(_endp, _buffer, _context);
|
||||||
|
|
||||||
if (read == -1)
|
if (read == -1)
|
||||||
{
|
{
|
||||||
_logger.debug("{}: client closed connection {}", this, _endPoint);
|
_logger.debug("{}: client closed connection {}", this, _endp);
|
||||||
close();
|
close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -666,8 +657,8 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
if (read == 0)
|
if (read == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
_logger.debug("{}: read from client {} bytes {}", this, read, _endPoint);
|
_logger.debug("{}: read from client {} bytes {}", this, read, _endp);
|
||||||
int written = write(_toServer._endPoint, _buffer, _context);
|
int written = write(_toServer.getEndPoint(), _buffer, _context);
|
||||||
_logger.debug("{}: written to {} {} bytes", this, _toServer, written);
|
_logger.debug("{}: written to {} {} bytes", this, _toServer, written);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
@ -696,11 +687,6 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeStamp()
|
|
||||||
{
|
|
||||||
return _timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isIdle()
|
public boolean isIdle()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -722,7 +708,7 @@ public class ConnectHandler extends HandlerWrapper
|
||||||
|
|
||||||
public void closeClient() throws IOException
|
public void closeClient() throws IOException
|
||||||
{
|
{
|
||||||
_endPoint.close();
|
_endp.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closeServer() throws IOException
|
public void closeServer() throws IOException
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package org.eclipse.jetty.util;
|
package org.eclipse.jetty.util;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
|
||||||
|
@ -39,6 +40,14 @@ public class StringUtil
|
||||||
public final static String __UTF8Alt="UTF8";
|
public final static String __UTF8Alt="UTF8";
|
||||||
public final static String __UTF16="UTF-16";
|
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 = {
|
private static char[] lowercases = {
|
||||||
'\000','\001','\002','\003','\004','\005','\006','\007',
|
'\000','\001','\002','\003','\004','\005','\006','\007',
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,13 +17,29 @@ import java.io.IOException;
|
||||||
|
|
||||||
public interface WebSocket
|
public interface WebSocket
|
||||||
{
|
{
|
||||||
|
@Deprecated
|
||||||
public final byte LENGTH_FRAME=(byte)0x80;
|
public final byte LENGTH_FRAME=(byte)0x80;
|
||||||
|
@Deprecated
|
||||||
public final byte SENTINEL_FRAME=(byte)0x00;
|
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 onConnect(Outbound outbound);
|
||||||
void onMessage(byte opcode,String data);
|
void onMessage(byte opcode,String data);
|
||||||
void onFragment(boolean more,byte opcode,byte[] data, int offset, int length);
|
void onFragment(boolean more,byte opcode,byte[] data, int offset, int length);
|
||||||
void onMessage(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
|
public interface Outbound
|
||||||
{
|
{
|
||||||
|
@ -32,6 +48,7 @@ public interface WebSocket
|
||||||
void sendMessage(byte opcode,byte[] data, int offset, int length) throws IOException;
|
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 sendFragment(boolean more,byte opcode,byte[] data, int offset, int length) throws IOException;
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
void disconnect(int code,String message);
|
||||||
boolean isOpen();
|
boolean isOpen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
|
||||||
public interface WebSocketConnection extends Connection, WebSocket.Outbound
|
public interface WebSocketConnection extends Connection
|
||||||
{
|
{
|
||||||
void fillBuffersFrom(Buffer buffer);
|
void fillBuffersFrom(Buffer buffer);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
import org.eclipse.jetty.io.ByteArrayBuffer;
|
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.io.nio.SelectChannelEndPoint;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
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 IdleCheck _idle;
|
||||||
final EndPoint _endp;
|
|
||||||
final WebSocketParser _parser;
|
final WebSocketParser _parser;
|
||||||
final WebSocketGenerator _generator;
|
final WebSocketGenerator _generator;
|
||||||
final long _timestamp;
|
|
||||||
final WebSocket _websocket;
|
final WebSocket _websocket;
|
||||||
String _key1;
|
String _key1;
|
||||||
String _key2;
|
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)
|
public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, int draft)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
super(endpoint,timestamp);
|
||||||
// TODO - can we use the endpoint idle mechanism?
|
// TODO - can we use the endpoint idle mechanism?
|
||||||
if (endpoint instanceof AsyncEndPoint)
|
if (endpoint instanceof AsyncEndPoint)
|
||||||
((AsyncEndPoint)endpoint).cancelIdle();
|
((AsyncEndPoint)endpoint).cancelIdle();
|
||||||
|
|
||||||
_endp = endpoint;
|
|
||||||
_endp.setMaxIdleTime(maxIdleTime);
|
_endp.setMaxIdleTime(maxIdleTime);
|
||||||
|
|
||||||
_timestamp = timestamp;
|
|
||||||
_websocket = websocket;
|
_websocket = websocket;
|
||||||
|
|
||||||
// Select the parser/generators to use
|
// Select the parser/generators to use
|
||||||
|
@ -220,11 +218,6 @@ public class WebSocketConnectionD00 implements WebSocketConnection
|
||||||
_websocket.onDisconnect();
|
_websocket.onDisconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeStamp()
|
|
||||||
{
|
|
||||||
return _timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(java.lang.String)
|
* @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
|
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();
|
_generator.flush();
|
||||||
checkWriteable();
|
checkWriteable();
|
||||||
_idle.access(_endp);
|
_idle.access(_endp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disconnect(int code, String message)
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -380,4 +378,5 @@ public class WebSocketConnectionD00 implements WebSocketConnection
|
||||||
_websocket.onConnect(this);
|
_websocket.onConnect(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -93,23 +93,30 @@ public class WebSocketFactory
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public void upgrade(HttpServletRequest request,HttpServletResponse response, WebSocket websocket, String origin, String subprotocol)
|
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");
|
throw new IllegalStateException("!Upgrade:websocket");
|
||||||
if (!"HTTP/1.1".equals(request.getProtocol()))
|
if (!"HTTP/1.1".equals(request.getProtocol()))
|
||||||
throw new IllegalStateException("!HTTP/1.1");
|
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();
|
HttpConnection http = HttpConnection.getCurrentConnection();
|
||||||
ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint();
|
ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint();
|
||||||
|
|
||||||
final WebSocketConnection connection;
|
final WebSocketConnection connection;
|
||||||
switch(draft)
|
switch(draft)
|
||||||
{
|
{
|
||||||
case 5:
|
case 6:
|
||||||
connection=new WebSocketConnectionD05(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft);
|
connection=new WebSocketConnectionD06(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft);
|
||||||
break;
|
break;
|
||||||
|
case 5:
|
||||||
|
case 4:
|
||||||
|
case 3:
|
||||||
|
case 2:
|
||||||
|
throw new UnsupportedOperationException("Unsupported draft specification: "+draft);
|
||||||
default:
|
default:
|
||||||
connection=new WebSocketConnectionD00(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft);
|
connection=new WebSocketConnectionD00(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,6 @@ public interface WebSocketGenerator
|
||||||
boolean isBufferEmpty();
|
boolean isBufferEmpty();
|
||||||
void addFrame(byte opcode, String content, int maxIdleTime) throws IOException;
|
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 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;
|
int flush(int maxIdleTime) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,9 +171,9 @@ public class WebSocketGeneratorD00 implements WebSocketGenerator
|
||||||
return _buffer==null || _buffer.length()==0;
|
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");
|
throw new UnsupportedOperationException("fragmented");
|
||||||
addFrame(opcode,content,offset,length,maxIdleTime);
|
addFrame(opcode,content,offset,length,maxIdleTime);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
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)
|
if (_buffer==null)
|
||||||
_buffer=_buffers.getDirectBuffer();
|
_buffer=_buffers.getDirectBuffer();
|
||||||
|
@ -69,10 +69,10 @@ public class WebSocketGeneratorD01 implements WebSocketGenerator
|
||||||
fragment=_buffer.capacity()-10;
|
fragment=_buffer.capacity()-10;
|
||||||
bufferPut((byte)(0x80|opcode), blockFor);
|
bufferPut((byte)(0x80|opcode), blockFor);
|
||||||
}
|
}
|
||||||
else if (more)
|
else if (last)
|
||||||
bufferPut((byte)(0x80|opcode), blockFor);
|
|
||||||
else
|
|
||||||
bufferPut(opcode, blockFor);
|
bufferPut(opcode, blockFor);
|
||||||
|
else
|
||||||
|
bufferPut((byte)(0x80|opcode), blockFor);
|
||||||
|
|
||||||
if (fragment>0xffff)
|
if (fragment>0xffff)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
package org.eclipse.jetty.websocket;
|
package org.eclipse.jetty.websocket;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
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 will call the addMessage methods while other
|
||||||
* threads are flushing the generator.
|
* threads are flushing the generator.
|
||||||
*/
|
*/
|
||||||
public class WebSocketGeneratorD05 implements WebSocketGenerator
|
public class WebSocketGeneratorD06 implements WebSocketGenerator
|
||||||
{
|
{
|
||||||
final private WebSocketBuffers _buffers;
|
final private WebSocketBuffers _buffers;
|
||||||
final private EndPoint _endp;
|
final private EndPoint _endp;
|
||||||
private Buffer _buffer;
|
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;
|
_buffers=buffers;
|
||||||
_endp=endp;
|
_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
|
public synchronized void addFrame(byte opcode,byte[] content, int blockFor) throws IOException
|
||||||
{
|
{
|
||||||
|
_opsent=false;
|
||||||
addFrame(opcode,content,0,content.length,blockFor);
|
addFrame(opcode,content,0,content.length,blockFor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public synchronized void addFrame(byte opcode,byte[] content, int offset, int length, int blockFor) throws IOException
|
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)
|
if (_buffer==null)
|
||||||
_buffer=_buffers.getDirectBuffer();
|
_buffer=_masked?_buffers.getBuffer():_buffers.getDirectBuffer();
|
||||||
|
|
||||||
if (_buffer.space() == 0)
|
int space=_masked?14:10;
|
||||||
expelBuffer(blockFor);
|
|
||||||
|
|
||||||
opcode = (byte)(opcode & 0x0f);
|
do
|
||||||
|
|
||||||
while (length>0)
|
|
||||||
{
|
{
|
||||||
// slice a fragment off
|
opcode = _opsent?WebSocket.OP_CONTINUATION:(byte)(opcode & 0x0f);
|
||||||
int fragment=length;
|
_opsent=true;
|
||||||
if (fragment+10>_buffer.capacity())
|
|
||||||
{
|
int payload=length;
|
||||||
fragment=_buffer.capacity()-10;
|
if (payload+space>_buffer.capacity())
|
||||||
bufferPut((byte)(0x80|opcode), blockFor);
|
payload=_buffer.capacity()-space;
|
||||||
}
|
else if (last)
|
||||||
else if (more)
|
opcode|=(byte)0x80; // Set the FIN bit
|
||||||
bufferPut((byte)(0x80|opcode), blockFor);
|
|
||||||
else
|
|
||||||
bufferPut(opcode, blockFor);
|
|
||||||
|
|
||||||
if (fragment>0xffff)
|
// ensure there is space for header
|
||||||
|
if (_buffer.space() <= space)
|
||||||
|
expelBuffer(blockFor);
|
||||||
|
|
||||||
|
// write mask
|
||||||
|
if (_masked)
|
||||||
{
|
{
|
||||||
bufferPut((byte)0x7f, blockFor);
|
_random.nextBytes(_mask);
|
||||||
bufferPut((byte)((fragment>>56)&0x7f), blockFor);
|
_m=0;
|
||||||
bufferPut((byte)((fragment>>48)&0xff), blockFor);
|
_buffer.put(_mask);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else if (fragment >=0x7e)
|
|
||||||
|
// write the opcode and length
|
||||||
|
if (payload>0xffff)
|
||||||
{
|
{
|
||||||
bufferPut((byte)126, blockFor);
|
bufferPut(new byte[]{
|
||||||
bufferPut((byte)(fragment>>8), blockFor);
|
opcode,
|
||||||
bufferPut((byte)(fragment&0xff), blockFor);
|
(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
|
else
|
||||||
{
|
{
|
||||||
bufferPut((byte)fragment, blockFor);
|
bufferPut(opcode);
|
||||||
|
bufferPut((byte)payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
int remaining = fragment;
|
// write payload
|
||||||
|
int remaining = payload;
|
||||||
while (remaining > 0)
|
while (remaining > 0)
|
||||||
{
|
{
|
||||||
_buffer.compact();
|
_buffer.compact();
|
||||||
int chunk = remaining < _buffer.space() ? remaining : _buffer.space();
|
int chunk = remaining < _buffer.space() ? remaining : _buffer.space();
|
||||||
_buffer.put(content, offset + (fragment - remaining), chunk);
|
|
||||||
|
if (_masked)
|
||||||
|
{
|
||||||
|
for (int i=0;i<chunk;i++)
|
||||||
|
bufferPut(content[offset+ (payload-remaining)+i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_buffer.put(content, offset + (payload - remaining), chunk);
|
||||||
|
|
||||||
remaining -= chunk;
|
remaining -= chunk;
|
||||||
if (_buffer.space() > 0)
|
if (_buffer.space() > 0)
|
||||||
{
|
{
|
||||||
|
@ -120,18 +152,24 @@ public class WebSocketGeneratorD05 implements WebSocketGenerator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
offset+=fragment;
|
offset+=payload;
|
||||||
length-=fragment;
|
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)
|
if (_masked)
|
||||||
_buffer=_buffers.getDirectBuffer();
|
for (int i=0;i<data.length;i++)
|
||||||
_buffer.put(datum);
|
data[i]^=_mask[+_m++%4];
|
||||||
if (_buffer.space() == 0)
|
_buffer.put(data);
|
||||||
expelBuffer(blockFor);
|
}
|
||||||
|
|
||||||
|
private synchronized void bufferPut(byte data) throws IOException
|
||||||
|
{
|
||||||
|
_buffer.put((byte)(data^_mask[+_m++%4]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void addFrame(byte frame, String content, int blockFor) throws IOException
|
public synchronized void addFrame(byte frame, String content, int blockFor) throws IOException
|
|
@ -95,9 +95,11 @@ public abstract class WebSocketHandler extends HandlerWrapper
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
if ("WebSocket".equals(request.getHeader("Upgrade")))
|
if ("websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
|
||||||
{
|
{
|
||||||
String subprotocol=request.getHeader(request.getHeader("Sec-WebSocket-Key1")!=null?"Sec-WebSocket-Protocol":"WebSocket-Protocol");
|
String subprotocol=request.getHeader("Sec-WebSocket-Protocol");
|
||||||
|
if (subprotocol==null) // TODO remove once draft period is over
|
||||||
|
subprotocol=request.getHeader("WebSocket-Protocol");
|
||||||
WebSocket websocket=doWebSocketConnect(request,subprotocol);
|
WebSocket websocket=doWebSocketConnect(request,subprotocol);
|
||||||
|
|
||||||
String host=request.getHeader("Host");
|
String host=request.getHeader("Host");
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.eclipse.jetty.util.log.Log;
|
||||||
* Parser the WebSocket protocol.
|
* Parser the WebSocket protocol.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class WebSocketParserD05 implements WebSocketParser
|
public class WebSocketParserD06 implements WebSocketParser
|
||||||
{
|
{
|
||||||
public enum State {
|
public enum State {
|
||||||
MASK(0), OPCODE(1), LENGTH_7(2), LENGTH_16(4), LENGTH_63(10), DATA(10);
|
MASK(0), OPCODE(1), LENGTH_7(2), LENGTH_16(4), LENGTH_63(10), DATA(10);
|
||||||
|
@ -53,7 +53,7 @@ public class WebSocketParserD05 implements WebSocketParser
|
||||||
private final boolean _masked;
|
private final boolean _masked;
|
||||||
private State _state;
|
private State _state;
|
||||||
private Buffer _buffer;
|
private Buffer _buffer;
|
||||||
private boolean _more;
|
private boolean _fin;
|
||||||
private byte _flags;
|
private byte _flags;
|
||||||
private byte _opcode;
|
private byte _opcode;
|
||||||
private int _count;
|
private int _count;
|
||||||
|
@ -70,7 +70,7 @@ public class WebSocketParserD05 implements WebSocketParser
|
||||||
* @param endp
|
* @param endp
|
||||||
* @param handler
|
* @param handler
|
||||||
*/
|
*/
|
||||||
public WebSocketParserD05(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean masked)
|
public WebSocketParserD06(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean masked)
|
||||||
{
|
{
|
||||||
_buffers=buffers;
|
_buffers=buffers;
|
||||||
_endp=endp;
|
_endp=endp;
|
||||||
|
@ -157,7 +157,7 @@ public class WebSocketParserD05 implements WebSocketParser
|
||||||
b^=_mask[_m++%4];
|
b^=_mask[_m++%4];
|
||||||
_opcode=(byte)(b&0xf);
|
_opcode=(byte)(b&0xf);
|
||||||
_flags=(byte)(b>>4);
|
_flags=(byte)(b>>4);
|
||||||
_more=(_flags&8)!=0;
|
_fin=(_flags&8)!=0;
|
||||||
_state=State.LENGTH_7;
|
_state=State.LENGTH_7;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ public class WebSocketParserD05 implements WebSocketParser
|
||||||
array[data.getIndex()+i]^=_mask[_m++%4];
|
array[data.getIndex()+i]^=_mask[_m++%4];
|
||||||
}
|
}
|
||||||
|
|
||||||
_handler.onFrame(_more,_flags, _opcode, data);
|
_handler.onFrame(!_fin,_flags, _opcode, data);
|
||||||
_count=0;
|
_count=0;
|
||||||
_state=State.OPCODE;
|
_state=State.OPCODE;
|
||||||
|
|
|
@ -64,9 +64,9 @@ public abstract class WebSocketServlet extends HttpServlet
|
||||||
{
|
{
|
||||||
boolean hixie = request.getHeader("Sec-WebSocket-Key1")!=null;
|
boolean hixie = request.getHeader("Sec-WebSocket-Key1")!=null;
|
||||||
|
|
||||||
String protocol=request.getHeader(hixie?"Sec-WebSocket-Protocol":"WebSocket-Protocol");
|
String protocol=request.getHeader("Sec-WebSocket-Protocol");
|
||||||
if (protocol==null)
|
if (protocol==null) // TODO remove once draft period is over
|
||||||
protocol=request.getHeader("Sec-WebSocket-Protocol");
|
protocol=request.getHeader("WebSocket-Protocol");
|
||||||
WebSocket websocket=doWebSocketConnect(request,protocol);
|
WebSocket websocket=doWebSocketConnect(request,protocol);
|
||||||
|
|
||||||
String host=request.getHeader("Host");
|
String host=request.getHeader("Host");
|
||||||
|
|
|
@ -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<b.length;i++)
|
||||||
|
b[i]=(byte)('0'+(i%10));
|
||||||
|
|
||||||
|
_generator.addFrame((byte)0x4,b,0,b.length,0);
|
||||||
|
|
||||||
|
_generator.flush();
|
||||||
|
assertEquals((byte)0x84,_out.get());
|
||||||
|
assertEquals((byte)126,_out.get());
|
||||||
|
assertEquals((byte)0,_out.get());
|
||||||
|
assertEquals((byte)b.length,_out.get());
|
||||||
|
|
||||||
|
for (int i=0;i<b.length;i++)
|
||||||
|
assertEquals('0'+(i%10),0xff&_out.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneStringMasked() throws Exception
|
||||||
|
{
|
||||||
|
_generator = new WebSocketGeneratorD06(_buffers, _endPoint,true);
|
||||||
|
|
||||||
|
_generator.addFrame((byte)0x04,"Hell\uFF4F W\uFF4Frld",0);
|
||||||
|
_generator.flush();
|
||||||
|
|
||||||
|
_out.get(_mask,0,4);
|
||||||
|
|
||||||
|
assertEquals((byte)0x84,getMasked());
|
||||||
|
assertEquals(15,0xff&getMasked());
|
||||||
|
assertEquals('H',getMasked());
|
||||||
|
assertEquals('e',getMasked());
|
||||||
|
assertEquals('l',getMasked());
|
||||||
|
assertEquals('l',getMasked());
|
||||||
|
assertEquals(0xEF,0xff&getMasked());
|
||||||
|
assertEquals(0xBD,0xff&getMasked());
|
||||||
|
assertEquals(0x8F,0xff&getMasked());
|
||||||
|
assertEquals(' ',getMasked());
|
||||||
|
assertEquals('W',getMasked());
|
||||||
|
assertEquals(0xEF,0xff&getMasked());
|
||||||
|
assertEquals(0xBD,0xff&getMasked());
|
||||||
|
assertEquals(0x8F,0xff&getMasked());
|
||||||
|
assertEquals('r',getMasked());
|
||||||
|
assertEquals('l',getMasked());
|
||||||
|
assertEquals('d',getMasked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneBufferMasked() throws Exception
|
||||||
|
{
|
||||||
|
_generator = new WebSocketGeneratorD06(_buffers, _endPoint,true);
|
||||||
|
|
||||||
|
String string = "Hell\uFF4F W\uFF4Frld";
|
||||||
|
byte[] bytes=string.getBytes(StringUtil.__UTF8);
|
||||||
|
_generator.addFrame((byte)0x04,bytes,0,bytes.length,0);
|
||||||
|
_generator.flush();
|
||||||
|
|
||||||
|
_out.get(_mask,0,4);
|
||||||
|
|
||||||
|
assertEquals((byte)0x84,getMasked());
|
||||||
|
assertEquals(15,0xff&getMasked());
|
||||||
|
assertEquals('H',getMasked());
|
||||||
|
assertEquals('e',getMasked());
|
||||||
|
assertEquals('l',getMasked());
|
||||||
|
assertEquals('l',getMasked());
|
||||||
|
assertEquals(0xEF,0xff&getMasked());
|
||||||
|
assertEquals(0xBD,0xff&getMasked());
|
||||||
|
assertEquals(0x8F,0xff&getMasked());
|
||||||
|
assertEquals(' ',getMasked());
|
||||||
|
assertEquals('W',getMasked());
|
||||||
|
assertEquals(0xEF,0xff&getMasked());
|
||||||
|
assertEquals(0xBD,0xff&getMasked());
|
||||||
|
assertEquals(0x8F,0xff&getMasked());
|
||||||
|
assertEquals('r',getMasked());
|
||||||
|
assertEquals('l',getMasked());
|
||||||
|
assertEquals('d',getMasked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneLongBufferMasked() throws Exception
|
||||||
|
{
|
||||||
|
_generator = new WebSocketGeneratorD06(_buffers, _endPoint,true);
|
||||||
|
|
||||||
|
byte[] b=new byte[150];
|
||||||
|
for (int i=0;i<b.length;i++)
|
||||||
|
b[i]=(byte)('0'+(i%10));
|
||||||
|
|
||||||
|
_generator.addFrame((byte)0x04,b,0,b.length,0);
|
||||||
|
_generator.flush();
|
||||||
|
|
||||||
|
_out.get(_mask,0,4);
|
||||||
|
assertEquals((byte)0x84,getMasked());
|
||||||
|
assertEquals((byte)126,getMasked());
|
||||||
|
assertEquals((byte)0,getMasked());
|
||||||
|
assertEquals((byte)b.length,getMasked());
|
||||||
|
|
||||||
|
for (int i=0;i<b.length;i++)
|
||||||
|
assertEquals('0'+(i%10),0xff&getMasked());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,12 +4,9 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
@ -21,8 +18,6 @@ import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||||
import org.eclipse.jetty.util.IO;
|
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
|
@ -0,0 +1,488 @@
|
||||||
|
package org.eclipse.jetty.websocket;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||||
|
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||||
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @version $Revision$ $Date$
|
||||||
|
*/
|
||||||
|
public class WebSocketMessageD06Test
|
||||||
|
{
|
||||||
|
private static Server _server;
|
||||||
|
private static Connector _connector;
|
||||||
|
private static TestWebSocket _serverWebSocket;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void startServer() throws Exception
|
||||||
|
{
|
||||||
|
_server = new Server();
|
||||||
|
_connector = new SelectChannelConnector();
|
||||||
|
_server.addConnector(_connector);
|
||||||
|
WebSocketHandler wsHandler = new WebSocketHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
|
||||||
|
{
|
||||||
|
_serverWebSocket = new TestWebSocket();
|
||||||
|
_serverWebSocket.onConnect=("onConnect".equals(protocol));
|
||||||
|
_serverWebSocket.echo=("echo".equals(protocol));
|
||||||
|
return _serverWebSocket;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
wsHandler.setMaxIdleTime(1000);
|
||||||
|
wsHandler.setHandler(new DefaultHandler());
|
||||||
|
_server.setHandler(wsHandler);
|
||||||
|
_server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stopServer() throws Exception
|
||||||
|
{
|
||||||
|
_server.stop();
|
||||||
|
_server.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHash()
|
||||||
|
{
|
||||||
|
assertEquals("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",WebSocketConnectionD06.hashKey("dGhlIHNhbXBsZSBub25jZQ=="));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testServerSendBigStringMessage() throws Exception
|
||||||
|
{
|
||||||
|
Socket socket = new Socket("localhost", _connector.getLocalPort());
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(
|
||||||
|
("GET /chat HTTP/1.1\r\n"+
|
||||||
|
"Host: server.example.com\r\n"+
|
||||||
|
"Upgrade: websocket\r\n"+
|
||||||
|
"Connection: Upgrade\r\n"+
|
||||||
|
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
|
||||||
|
"Sec-WebSocket-Origin: http://example.com\r\n"+
|
||||||
|
"Sec-WebSocket-Protocol: chat, superchat\r\n"+
|
||||||
|
"Sec-WebSocket-Version: 6\r\n"+
|
||||||
|
"\r\n").getBytes("ISO-8859-1"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Make sure the read times out if there are problems with the implementation
|
||||||
|
socket.setSoTimeout(1000);
|
||||||
|
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
|
||||||
|
lookFor("HTTP/1.1 101 Switching Protocols\r\n",input);
|
||||||
|
skipTo("Sec-WebSocket-Accept: ",input);
|
||||||
|
lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input);
|
||||||
|
skipTo("\r\n\r\n",input);
|
||||||
|
|
||||||
|
assertTrue(_serverWebSocket.awaitConnected(1000));
|
||||||
|
assertNotNull(_serverWebSocket.outbound);
|
||||||
|
|
||||||
|
// Server sends a big message
|
||||||
|
StringBuilder message = new StringBuilder();
|
||||||
|
String text = "0123456789ABCDEF";
|
||||||
|
for (int i = 0; i < (0x2000) / text.length(); i++)
|
||||||
|
message.append(text);
|
||||||
|
String data=message.toString();
|
||||||
|
_serverWebSocket.outbound.sendMessage(data);
|
||||||
|
|
||||||
|
assertEquals(WebSocket.OP_TEXT,input.read());
|
||||||
|
assertEquals(0x7e,input.read());
|
||||||
|
assertEquals(0x1f,input.read());
|
||||||
|
assertEquals(0xf6,input.read());
|
||||||
|
lookFor(data.substring(0,0x1ff6),input);
|
||||||
|
assertEquals(0x80,input.read());
|
||||||
|
assertEquals(0x0A,input.read());
|
||||||
|
lookFor(data.substring(0x1ff6),input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testServerSendOnConnect() throws Exception
|
||||||
|
{
|
||||||
|
Socket socket = new Socket("localhost", _connector.getLocalPort());
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(
|
||||||
|
("GET /chat HTTP/1.1\r\n"+
|
||||||
|
"Host: server.example.com\r\n"+
|
||||||
|
"Upgrade: websocket\r\n"+
|
||||||
|
"Connection: Upgrade\r\n"+
|
||||||
|
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
|
||||||
|
"Sec-WebSocket-Origin: http://example.com\r\n"+
|
||||||
|
"Sec-WebSocket-Protocol: onConnect\r\n" +
|
||||||
|
"Sec-WebSocket-Version: 6\r\n"+
|
||||||
|
"\r\n").getBytes("ISO-8859-1"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Make sure the read times out if there are problems with the implementation
|
||||||
|
socket.setSoTimeout(1000);
|
||||||
|
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
|
||||||
|
lookFor("HTTP/1.1 101 Switching Protocols\r\n",input);
|
||||||
|
skipTo("Sec-WebSocket-Accept: ",input);
|
||||||
|
lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input);
|
||||||
|
skipTo("\r\n\r\n",input);
|
||||||
|
|
||||||
|
assertTrue(_serverWebSocket.awaitConnected(1000));
|
||||||
|
assertNotNull(_serverWebSocket.outbound);
|
||||||
|
|
||||||
|
assertEquals(0x84,input.read());
|
||||||
|
assertEquals(0x0f,input.read());
|
||||||
|
lookFor("sent on connect",input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testServerEcho() throws Exception
|
||||||
|
{
|
||||||
|
Socket socket = new Socket("localhost", _connector.getLocalPort());
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(
|
||||||
|
("GET /chat HTTP/1.1\r\n"+
|
||||||
|
"Host: server.example.com\r\n"+
|
||||||
|
"Upgrade: websocket\r\n"+
|
||||||
|
"Connection: Upgrade\r\n"+
|
||||||
|
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
|
||||||
|
"Sec-WebSocket-Origin: http://example.com\r\n"+
|
||||||
|
"Sec-WebSocket-Protocol: echo\r\n" +
|
||||||
|
"Sec-WebSocket-Version: 6\r\n"+
|
||||||
|
"\r\n").getBytes("ISO-8859-1"));
|
||||||
|
output.flush();
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0x84^0xff);
|
||||||
|
output.write(0x0f^0xff);
|
||||||
|
byte[] bytes="this is an echo".getBytes(StringUtil.__ISO_8859_1_CHARSET);
|
||||||
|
for (int i=0;i<bytes.length;i++)
|
||||||
|
output.write(bytes[i]^0xff);
|
||||||
|
output.flush();
|
||||||
|
// Make sure the read times out if there are problems with the implementation
|
||||||
|
socket.setSoTimeout(1000);
|
||||||
|
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
|
||||||
|
lookFor("HTTP/1.1 101 Switching Protocols\r\n",input);
|
||||||
|
skipTo("Sec-WebSocket-Accept: ",input);
|
||||||
|
lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input);
|
||||||
|
skipTo("\r\n\r\n",input);
|
||||||
|
|
||||||
|
assertTrue(_serverWebSocket.awaitConnected(1000));
|
||||||
|
assertNotNull(_serverWebSocket.outbound);
|
||||||
|
|
||||||
|
assertEquals(0x84,input.read());
|
||||||
|
assertEquals(0x0f,input.read());
|
||||||
|
lookFor("this is an echo",input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testServerPingPong() throws Exception
|
||||||
|
{
|
||||||
|
Socket socket = new Socket("localhost", _connector.getLocalPort());
|
||||||
|
// Make sure the read times out if there are problems with the implementation
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(
|
||||||
|
("GET /chat HTTP/1.1\r\n"+
|
||||||
|
"Host: server.example.com\r\n"+
|
||||||
|
"Upgrade: websocket\r\n"+
|
||||||
|
"Connection: Upgrade\r\n"+
|
||||||
|
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
|
||||||
|
"Sec-WebSocket-Origin: http://example.com\r\n"+
|
||||||
|
"Sec-WebSocket-Protocol: echo\r\n" +
|
||||||
|
"Sec-WebSocket-Version: 6\r\n"+
|
||||||
|
"\r\n").getBytes("ISO-8859-1"));
|
||||||
|
output.flush();
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0x82^0xff);
|
||||||
|
output.write(0x00^0xff);
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
|
||||||
|
lookFor("HTTP/1.1 101 Switching Protocols\r\n",input);
|
||||||
|
skipTo("Sec-WebSocket-Accept: ",input);
|
||||||
|
lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input);
|
||||||
|
skipTo("\r\n\r\n",input);
|
||||||
|
|
||||||
|
assertTrue(_serverWebSocket.awaitConnected(1000));
|
||||||
|
assertNotNull(_serverWebSocket.outbound);
|
||||||
|
|
||||||
|
socket.setSoTimeout(1000);
|
||||||
|
assertEquals(0x83,input.read());
|
||||||
|
assertEquals(0x00,input.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIdle() throws Exception
|
||||||
|
{
|
||||||
|
Socket socket = new Socket("localhost", _connector.getLocalPort());
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(
|
||||||
|
("GET /chat HTTP/1.1\r\n"+
|
||||||
|
"Host: server.example.com\r\n"+
|
||||||
|
"Upgrade: websocket\r\n"+
|
||||||
|
"Connection: Upgrade\r\n"+
|
||||||
|
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
|
||||||
|
"Sec-WebSocket-Origin: http://example.com\r\n"+
|
||||||
|
"Sec-WebSocket-Protocol: onConnect\r\n" +
|
||||||
|
"Sec-WebSocket-Version: 6\r\n"+
|
||||||
|
"\r\n").getBytes("ISO-8859-1"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Make sure the read times out if there are problems with the implementation
|
||||||
|
socket.setSoTimeout(10000);
|
||||||
|
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
|
||||||
|
lookFor("HTTP/1.1 101 Switching Protocols\r\n",input);
|
||||||
|
skipTo("Sec-WebSocket-Accept: ",input);
|
||||||
|
lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input);
|
||||||
|
skipTo("\r\n\r\n",input);
|
||||||
|
|
||||||
|
assertTrue(_serverWebSocket.awaitConnected(1000));
|
||||||
|
assertNotNull(_serverWebSocket.outbound);
|
||||||
|
|
||||||
|
assertEquals(0x84,input.read());
|
||||||
|
assertEquals(0x0f,input.read());
|
||||||
|
lookFor("sent on connect",input);
|
||||||
|
|
||||||
|
assertEquals((byte)0x81,(byte)input.read());
|
||||||
|
assertEquals(0x06,input.read());
|
||||||
|
assertEquals(1000/0xff,input.read());
|
||||||
|
assertEquals(1000%0xff,input.read());
|
||||||
|
lookFor("Idle",input);
|
||||||
|
|
||||||
|
// respond to close
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0xff);
|
||||||
|
output.write(0x81^0xff);
|
||||||
|
output.write(0x00^0xff);
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
|
||||||
|
assertTrue(_serverWebSocket.awaitDisconnected(5000));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_serverWebSocket.outbound.sendMessage("Don't send");
|
||||||
|
assertTrue(false);
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClose() throws Exception
|
||||||
|
{
|
||||||
|
Socket socket = new Socket("localhost", _connector.getLocalPort());
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(
|
||||||
|
("GET /chat HTTP/1.1\r\n"+
|
||||||
|
"Host: server.example.com\r\n"+
|
||||||
|
"Upgrade: websocket\r\n"+
|
||||||
|
"Connection: Upgrade\r\n"+
|
||||||
|
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
|
||||||
|
"Sec-WebSocket-Origin: http://example.com\r\n"+
|
||||||
|
"Sec-WebSocket-Protocol: onConnect\r\n" +
|
||||||
|
"Sec-WebSocket-Version: 6\r\n"+
|
||||||
|
"\r\n").getBytes("ISO-8859-1"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Make sure the read times out if there are problems with the implementation
|
||||||
|
socket.setSoTimeout(1000);
|
||||||
|
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
|
||||||
|
lookFor("HTTP/1.1 101 Switching Protocols\r\n",input);
|
||||||
|
skipTo("Sec-WebSocket-Accept: ",input);
|
||||||
|
lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input);
|
||||||
|
skipTo("\r\n\r\n",input);
|
||||||
|
|
||||||
|
|
||||||
|
assertTrue(_serverWebSocket.awaitConnected(1000));
|
||||||
|
assertNotNull(_serverWebSocket.outbound);
|
||||||
|
|
||||||
|
assertEquals(0x84,input.read());
|
||||||
|
assertEquals(0x0f,input.read());
|
||||||
|
lookFor("sent on connect",input);
|
||||||
|
socket.close();
|
||||||
|
|
||||||
|
assertTrue(_serverWebSocket.awaitDisconnected(500));
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_serverWebSocket.outbound.sendMessage("Don't send");
|
||||||
|
assertTrue(false);
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
private void lookFor(String string,InputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
String orig=string;
|
||||||
|
Utf8StringBuilder scanned=new Utf8StringBuilder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int b = in.read();
|
||||||
|
if (b<0)
|
||||||
|
throw new EOFException();
|
||||||
|
scanned.append((byte)b);
|
||||||
|
assertEquals("looking for\""+orig+"\" in '"+scanned+"'",(int)string.charAt(0),b);
|
||||||
|
if (string.length()==1)
|
||||||
|
break;
|
||||||
|
string=string.substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
System.err.println("IOE while looking for \""+orig+"\" in '"+scanned+"'");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipTo(String string,InputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
int state=0;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int b = in.read();
|
||||||
|
if (b<0)
|
||||||
|
throw new EOFException();
|
||||||
|
|
||||||
|
if (b==string.charAt(state))
|
||||||
|
{
|
||||||
|
state++;
|
||||||
|
if (state==string.length())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
state=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class TestWebSocket implements WebSocket
|
||||||
|
{
|
||||||
|
boolean onConnect=false;
|
||||||
|
boolean echo=true;
|
||||||
|
private final CountDownLatch connected = new CountDownLatch(1);
|
||||||
|
private final CountDownLatch disconnected = new CountDownLatch(1);
|
||||||
|
private volatile Outbound outbound;
|
||||||
|
|
||||||
|
public void onConnect(Outbound outbound)
|
||||||
|
{
|
||||||
|
this.outbound = outbound;
|
||||||
|
if (onConnect)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
outbound.sendMessage("sent on connect");
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connected.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean awaitConnected(long time) throws InterruptedException
|
||||||
|
{
|
||||||
|
return connected.await(time, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean awaitDisconnected(long time) throws InterruptedException
|
||||||
|
{
|
||||||
|
return disconnected.await(time, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMessage(byte opcode, String data)
|
||||||
|
{
|
||||||
|
if (echo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
outbound.sendMessage(opcode,data);
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMessage(byte opcode, byte[] data, int offset, int length)
|
||||||
|
{
|
||||||
|
if (echo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
outbound.sendMessage(opcode,data,offset,length);
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDisconnect()
|
||||||
|
{
|
||||||
|
disconnected.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFragment(boolean more, byte opcode, byte[] data, int offset, int length)
|
||||||
|
{
|
||||||
|
if (echo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
outbound.sendFragment(more,opcode,data,offset,length);
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ import org.junit.Test;
|
||||||
/**
|
/**
|
||||||
* @version $Revision$ $Date$
|
* @version $Revision$ $Date$
|
||||||
*/
|
*/
|
||||||
public class WebSocketParserD05Test
|
public class WebSocketParserD06Test
|
||||||
{
|
{
|
||||||
private ByteArrayBuffer _in;
|
private ByteArrayBuffer _in;
|
||||||
private Handler _handler;
|
private Handler _handler;
|
||||||
|
@ -33,7 +33,7 @@ public class WebSocketParserD05Test
|
||||||
WebSocketBuffers buffers = new WebSocketBuffers(1024);
|
WebSocketBuffers buffers = new WebSocketBuffers(1024);
|
||||||
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
|
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
|
||||||
_handler = new Handler();
|
_handler = new Handler();
|
||||||
_parser=new WebSocketParserD05(buffers, endPoint,_handler,true);
|
_parser=new WebSocketParserD06(buffers, endPoint,_handler,true);
|
||||||
_in = new ByteArrayBuffer(2048)
|
_in = new ByteArrayBuffer(2048)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -104,7 +104,7 @@ public class WebSocketParserD05Test
|
||||||
@Test
|
@Test
|
||||||
public void testShortText() throws Exception
|
public void testShortText() throws Exception
|
||||||
{
|
{
|
||||||
_in.put((byte)0x00);
|
_in.put((byte)0x84);
|
||||||
_in.put((byte)11);
|
_in.put((byte)11);
|
||||||
_in.put("Hello World".getBytes(StringUtil.__UTF8));
|
_in.put("Hello World".getBytes(StringUtil.__UTF8));
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ public class WebSocketParserD05Test
|
||||||
String string = "Hell\uFF4f W\uFF4Frld";
|
String string = "Hell\uFF4f W\uFF4Frld";
|
||||||
byte[] bytes = string.getBytes("UTF-8");
|
byte[] bytes = string.getBytes("UTF-8");
|
||||||
|
|
||||||
_in.put((byte)0x00);
|
_in.put((byte)0x84);
|
||||||
_in.put((byte)bytes.length);
|
_in.put((byte)bytes.length);
|
||||||
_in.put(bytes);
|
_in.put(bytes);
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ public class WebSocketParserD05Test
|
||||||
|
|
||||||
byte[] bytes = string.getBytes("UTF-8");
|
byte[] bytes = string.getBytes("UTF-8");
|
||||||
|
|
||||||
_in.put((byte)0x00);
|
_in.put((byte)0x84);
|
||||||
_in.put((byte)0x7E);
|
_in.put((byte)0x7E);
|
||||||
_in.put((byte)(bytes.length>>8));
|
_in.put((byte)(bytes.length>>8));
|
||||||
_in.put((byte)(bytes.length&0xff));
|
_in.put((byte)(bytes.length&0xff));
|
||||||
|
@ -164,7 +164,7 @@ public class WebSocketParserD05Test
|
||||||
|
|
||||||
WebSocketBuffers buffers = new WebSocketBuffers(0x20000);
|
WebSocketBuffers buffers = new WebSocketBuffers(0x20000);
|
||||||
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
|
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
|
||||||
WebSocketParser parser=new WebSocketParserD01(buffers, endPoint,_handler);
|
WebSocketParser parser=new WebSocketParserD06(buffers, endPoint,_handler,false);
|
||||||
ByteArrayBuffer in = new ByteArrayBuffer(0x20000);
|
ByteArrayBuffer in = new ByteArrayBuffer(0x20000);
|
||||||
endPoint.setIn(in);
|
endPoint.setIn(in);
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ public class WebSocketParserD05Test
|
||||||
|
|
||||||
byte[] bytes = string.getBytes("UTF-8");
|
byte[] bytes = string.getBytes("UTF-8");
|
||||||
|
|
||||||
in.put((byte)0x00);
|
in.put((byte)0x84);
|
||||||
in.put((byte)0x7F);
|
in.put((byte)0x7F);
|
||||||
in.put((byte)0x00);
|
in.put((byte)0x00);
|
||||||
in.put((byte)0x00);
|
in.put((byte)0x00);
|
||||||
|
@ -199,10 +199,10 @@ public class WebSocketParserD05Test
|
||||||
@Test
|
@Test
|
||||||
public void testShortFragmentTest() throws Exception
|
public void testShortFragmentTest() throws Exception
|
||||||
{
|
{
|
||||||
_in.put((byte)0x80);
|
_in.put((byte)0x04);
|
||||||
_in.put((byte)0x06);
|
_in.put((byte)0x06);
|
||||||
_in.put("Hello ".getBytes(StringUtil.__UTF8));
|
_in.put("Hello ".getBytes(StringUtil.__UTF8));
|
||||||
_in.put((byte)0x00);
|
_in.put((byte)0x80);
|
||||||
_in.put((byte)0x05);
|
_in.put((byte)0x05);
|
||||||
_in.put("World".getBytes(StringUtil.__UTF8));
|
_in.put("World".getBytes(StringUtil.__UTF8));
|
||||||
|
|
||||||
|
@ -232,11 +232,11 @@ public class WebSocketParserD05Test
|
||||||
if (more)
|
if (more)
|
||||||
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
|
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
|
||||||
else if (_utf8.length()==0)
|
else if (_utf8.length()==0)
|
||||||
_data.add(opcode,buffer.toString("utf-8"));
|
_data.add(buffer.toString("utf-8"));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
|
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
|
||||||
_data.add(opcode,_utf8.toString());
|
_data.add(_utf8.toString());
|
||||||
_utf8.reset();
|
_utf8.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue