jetty-9 starting to cleanup websocket and client

This commit is contained in:
Greg Wilkins 2012-06-07 14:11:40 +02:00
parent d1bc48de70
commit 0b56e3ae7c
30 changed files with 18 additions and 8593 deletions

View File

@ -106,12 +106,13 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!--
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-websocket</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependency>-->
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>

View File

@ -16,6 +16,7 @@ package org.eclipse.jetty.client;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.client.security.SecurityListener;
@ -26,9 +27,6 @@ import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.BufferCache.ByteBuffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -88,10 +86,10 @@ public class HttpExchange
public static final int STATUS_CANCELLED = 11;
// HTTP protocol fields
private String _method = HttpMethod.GET;
private ByteBuffer _scheme = HttpScheme.HTTP_BUFFER;
private String _method = HttpMethod.GET.asString();
private String _scheme = HttpScheme.HTTP.asString();
private String _uri;
private int _version = HttpVersion.HTTP_1_1_ORDINAL;
private HttpVersion _version = HttpVersion.HTTP_1_1;
private Address _address;
private final HttpFields _requestFields = new HttpFields();
private ByteBuffer _requestContent;
@ -459,32 +457,16 @@ public class HttpExchange
* @param scheme
* the scheme of the URL (for example 'http')
*/
public void setScheme(ByteBuffer scheme)
public void setScheme(String scheme)
{
_scheme = scheme;
}
/**
* @param scheme
* the scheme of the URL (for example 'http')
*/
public void setScheme(String scheme)
{
if (scheme != null)
{
if (HttpScheme.HTTP.equalsIgnoreCase(scheme))
setScheme(HttpScheme.HTTP_BUFFER);
else if (HttpScheme.HTTPS.equalsIgnoreCase(scheme))
setScheme(HttpScheme.HTTPS_BUFFER);
else
setScheme(new ByteArrayBuffer(scheme));
}
}
/**
* @return the scheme of the URL
*/
public ByteBuffer getScheme()
public String getScheme()
{
return _scheme;
}
@ -493,29 +475,16 @@ public class HttpExchange
* @param version
* the HTTP protocol version as integer, 9, 10 or 11 for 0.9, 1.0 or 1.1
*/
public void setVersion(int version)
public void setVersion(HttpVersion version)
{
_version = version;
}
/**
* @param version
* the HTTP protocol version as string
*/
public void setVersion(String version)
{
CachedBuffer v = HttpVersion.CACHE.get(version);
if (v == null)
_version = 10;
else
_version = v.getOrdinal();
}
/**
* @return the HTTP protocol version as integer
* @see #setVersion(int)
*/
public int getVersion()
public HttpVersion getVersion()
{
return _version;
}
@ -634,19 +603,6 @@ public class HttpExchange
getRequestFields().add(name,value);
}
/**
* Adds the specified request header
*
* @param name
* the header name
* @param value
* the header value
*/
public void addRequestHeader(ByteBuffer name, ByteBuffer value)
{
getRequestFields().add(name,value);
}
/**
* Sets the specified request header
*
@ -660,26 +616,13 @@ public class HttpExchange
getRequestFields().put(name,value);
}
/**
* Sets the specified request header
*
* @param name
* the header name
* @param value
* the header value
*/
public void setRequestHeader(ByteBuffer name, ByteBuffer value)
{
getRequestFields().put(name,value);
}
/**
* @param value
* the content type of the request
*/
public void setRequestContentType(String value)
{
getRequestFields().put(HttpHeader.CONTENT_TYPE_BUFFER,value);
getRequestFields().put(HttpHeader.CONTENT_TYPE,value);
}
/**

View File

@ -1,75 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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.Buffers;
import org.eclipse.jetty.io.Buffers.Type;
import org.eclipse.jetty.io.BuffersFactory;
/* ------------------------------------------------------------ */
/** The WebSocket Buffer Pool.
*
* The normal buffers are byte array buffers so that user processes
* can access directly. However the generator uses direct buffers
* for the final output stage as they are filled in bulk and are more
* efficient to flush.
*/
public class WebSocketBuffers
{
final private int _bufferSize;
final private Buffers _buffers;
public WebSocketBuffers(final int bufferSize)
{
_bufferSize=bufferSize;
_buffers = BuffersFactory.newBuffers(Type.DIRECT,bufferSize,Type.INDIRECT,bufferSize,Type.INDIRECT,-1);
}
public ByteBuffer getBuffer()
{
return _buffers.getBuffer();
}
public ByteBuffer getDirectBuffer()
{
return _buffers.getHeader();
}
public void returnBuffer(ByteBuffer buffer)
{
_buffers.returnBuffer(buffer);
}
public int getBufferSize()
{
return _bufferSize;
}
}

View File

@ -16,10 +16,12 @@
package org.eclipse.jetty.websocket;
import java.nio.ByteBuffer;
import java.util.List;
import org.eclipse.jetty.io.AsyncConnection;
import org.eclipse.jetty.io.nio.AsyncConnection;
public interface WebSocketConnection extends AsyncConnection
{

View File

@ -1,521 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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 java.util.Collections;
import java.util.List;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.WebSocket.OnFrame;
public class WebSocketConnectionD00 extends AbstractConnection implements WebSocketConnection, WebSocket.FrameConnection
{
private static final Logger LOG = Log.getLogger(WebSocketConnectionD00.class);
public final static byte LENGTH_FRAME=(byte)0x80;
public final static byte SENTINEL_FRAME=(byte)0x00;
private final WebSocketParser _parser;
private final WebSocketGenerator _generator;
private final WebSocket _websocket;
private final String _protocol;
private String _key1;
private String _key2;
private ByteArrayBuffer _hixieBytes;
public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
throws IOException
{
super(endpoint,timestamp);
_endp.setMaxIdleTime(maxIdleTime);
_websocket = websocket;
_protocol=protocol;
_generator = new WebSocketGeneratorD00(buffers, _endp);
_parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD00(_websocket));
}
/* ------------------------------------------------------------ */
public org.eclipse.jetty.websocket.WebSocket.Connection getConnection()
{
return this;
}
/* ------------------------------------------------------------ */
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
ByteBuffer 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;
}
}
if (_websocket instanceof OnFrame)
((OnFrame)_websocket).onHandshake(this);
_websocket.onOpen(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;
_endp.flush();
if (_endp instanceof AsyncEndPoint && ((AsyncEndPoint)_endp).hasProgressed())
progress=true;
}
}
catch(IOException e)
{
LOG.debug(e);
try
{
if (_endp.isOpen())
_endp.close();
}
catch(IOException e2)
{
LOG.ignore(e2);
}
throw e;
}
finally
{
if (_endp.isOpen())
{
if (_endp.isInputShutdown() && _generator.isBufferEmpty())
_endp.close();
else
checkWriteable();
checkWriteable();
}
}
return this;
}
/* ------------------------------------------------------------ */
public void onInputShutdown() throws IOException
{
// TODO
}
/* ------------------------------------------------------------ */
private void doTheHixieHixieShake()
{
byte[] result=WebSocketConnectionD00.doTheHixieHixieShake(
WebSocketConnectionD00.hixieCrypt(_key1),
WebSocketConnectionD00.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 onClose()
{
_websocket.onClose(WebSocketConnectionD06.CLOSE_NORMAL,"");
}
/* ------------------------------------------------------------ */
/**
*/
public void sendMessage(String content) throws IOException
{
byte[] data = content.getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0,SENTINEL_FRAME,data,0,data.length);
_generator.flush();
checkWriteable();
}
/* ------------------------------------------------------------ */
public void sendMessage(byte[] data, int offset, int length) throws IOException
{
_generator.addFrame((byte)0,LENGTH_FRAME,data,offset,length);
_generator.flush();
checkWriteable();
}
/* ------------------------------------------------------------ */
public boolean isMore(byte flags)
{
return (flags&0x8) != 0;
}
/* ------------------------------------------------------------ */
/**
* {@inheritDoc}
*/
public void sendControl(byte code, byte[] content, int offset, int length) throws IOException
{
}
/* ------------------------------------------------------------ */
public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
{
_generator.addFrame((byte)0,opcode,content,offset,length);
_generator.flush();
checkWriteable();
}
/* ------------------------------------------------------------ */
public void close(int code, String message)
{
throw new UnsupportedOperationException();
}
/* ------------------------------------------------------------ */
public void disconnect()
{
close();
}
/* ------------------------------------------------------------ */
public void close()
{
try
{
_generator.flush();
_endp.close();
}
catch(IOException e)
{
LOG.ignore(e);
}
}
public void shutdown()
{
close();
}
/* ------------------------------------------------------------ */
public void fillBuffersFrom(ByteBuffer 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);
System.arraycopy(key3, 0, fodder, 8, 8);
md.update(fodder);
return md.digest();
}
catch (NoSuchAlgorithmException e)
{
throw new IllegalStateException(e);
}
}
public void setMaxTextMessageSize(int size)
{
}
public void setMaxIdleTime(int ms)
{
try
{
_endp.setMaxIdleTime(ms);
}
catch(IOException e)
{
LOG.warn(e);
}
}
public void setMaxBinaryMessageSize(int size)
{
}
public int getMaxTextMessageSize()
{
return -1;
}
public int getMaxIdleTime()
{
return _endp.getMaxIdleTime();
}
public int getMaxBinaryMessageSize()
{
return -1;
}
public String getProtocol()
{
return _protocol;
}
protected void onFrameHandshake()
{
if (_websocket instanceof OnFrame)
{
((OnFrame)_websocket).onHandshake(this);
}
}
protected void onWebsocketOpen()
{
_websocket.onOpen(this);
}
static class FrameHandlerD00 implements WebSocketParser.FrameHandler
{
final WebSocket _websocket;
FrameHandlerD00(WebSocket websocket)
{
_websocket=websocket;
}
public void onFrame(byte flags, byte opcode, ByteBuffer buffer)
{
try
{
byte[] array=buffer.array();
if (opcode==0)
{
if (_websocket instanceof WebSocket.OnTextMessage)
((WebSocket.OnTextMessage)_websocket).onMessage(buffer.toString(StringUtil.__UTF8));
}
else
{
if (_websocket instanceof WebSocket.OnBinaryMessage)
((WebSocket.OnBinaryMessage)_websocket).onMessage(array,buffer.getIndex(),buffer.length());
}
}
catch(Throwable th)
{
LOG.warn(th);
}
}
public void close(int code,String message)
{
}
}
public boolean isMessageComplete(byte flags)
{
return true;
}
public byte binaryOpcode()
{
return LENGTH_FRAME;
}
public byte textOpcode()
{
return SENTINEL_FRAME;
}
public boolean isControl(byte opcode)
{
return false;
}
public boolean isText(byte opcode)
{
return (opcode&LENGTH_FRAME)==0;
}
public boolean isBinary(byte opcode)
{
return (opcode&LENGTH_FRAME)!=0;
}
public boolean isContinuation(byte opcode)
{
return false;
}
public boolean isClose(byte opcode)
{
return false;
}
public boolean isPing(byte opcode)
{
return false;
}
public boolean isPong(byte opcode)
{
return false;
}
public List<Extension> getExtensions()
{
return Collections.emptyList();
}
public byte continuationOpcode()
{
return 0;
}
public byte finMask()
{
return 0;
}
public void setAllowFrameFragmentation(boolean allowFragmentation)
{
}
public boolean isAllowFrameFragmentation()
{
return false;
}
}

View File

@ -1,744 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.List;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
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;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.WebSocket.OnBinaryMessage;
import org.eclipse.jetty.websocket.WebSocket.OnControl;
import org.eclipse.jetty.websocket.WebSocket.OnFrame;
import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
public class WebSocketConnectionD06 extends AbstractConnection implements WebSocketConnection
{
private static final Logger LOG = Log.getLogger(WebSocketConnectionD06.class);
final static byte OP_CONTINUATION = 0x00;
final static byte OP_CLOSE = 0x01;
final static byte OP_PING = 0x02;
final static byte OP_PONG = 0x03;
final static byte OP_TEXT = 0x04;
final static byte OP_BINARY = 0x05;
final static int CLOSE_NORMAL=1000;
final static int CLOSE_SHUTDOWN=1001;
final static int CLOSE_PROTOCOL=1002;
final static int CLOSE_BADDATA=1003;
final static int CLOSE_LARGE=1004;
static boolean isLastFrame(int flags)
{
return (flags&0x8)!=0;
}
static boolean isControlFrame(int opcode)
{
switch(opcode)
{
case OP_CLOSE:
case OP_PING:
case OP_PONG:
return true;
default:
return false;
}
}
private final static byte[] MAGIC;
private final WebSocketParser _parser;
private final WebSocketGenerator _generator;
private final WebSocket _webSocket;
private final OnFrame _onFrame;
private final OnBinaryMessage _onBinaryMessage;
private final OnTextMessage _onTextMessage;
private final OnControl _onControl;
private final String _protocol;
private volatile boolean _closedIn;
private volatile boolean _closedOut;
private int _maxTextMessageSize;
private int _maxBinaryMessageSize=-1;
static
{
try
{
MAGIC="258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(StringUtil.__ISO_8859_1);
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
}
private final WebSocketParser.FrameHandler _frameHandler= new FrameHandlerD06();
private final WebSocket.FrameConnection _connection = new FrameConnectionD06();
/* ------------------------------------------------------------ */
public WebSocketConnectionD06(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
throws IOException
{
super(endpoint,timestamp);
_endp.setMaxIdleTime(maxIdleTime);
_webSocket = websocket;
_onFrame=_webSocket instanceof OnFrame ? (OnFrame)_webSocket : null;
_onTextMessage=_webSocket instanceof OnTextMessage ? (OnTextMessage)_webSocket : null;
_onBinaryMessage=_webSocket instanceof OnBinaryMessage ? (OnBinaryMessage)_webSocket : null;
_onControl=_webSocket instanceof OnControl ? (OnControl)_webSocket : null;
_generator = new WebSocketGeneratorD06(buffers, _endp,null);
_parser = new WebSocketParserD06(buffers, endpoint, _frameHandler,true);
_protocol=protocol;
_maxTextMessageSize=buffers.getBufferSize();
_maxBinaryMessageSize=-1;
}
/* ------------------------------------------------------------ */
public WebSocket.Connection getConnection()
{
return _connection;
}
/* ------------------------------------------------------------ */
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())
{
if (_closedIn && _closedOut && _generator.isBufferEmpty())
_endp.close();
else if (_endp.isInputShutdown() && !_closedIn)
closeIn(CLOSE_PROTOCOL,null);
else
checkWriteable();
}
}
return this;
}
/* ------------------------------------------------------------ */
public void onInputShutdown() throws IOException
{
// TODO
}
/* ------------------------------------------------------------ */
public boolean isIdle()
{
return _parser.isBufferEmpty() && _generator.isBufferEmpty();
}
/* ------------------------------------------------------------ */
@Override
public void onIdleExpired(long idleForMs)
{
closeOut(WebSocketConnectionD06.CLOSE_NORMAL,"Idle");
}
/* ------------------------------------------------------------ */
public boolean isSuspended()
{
return false;
}
/* ------------------------------------------------------------ */
public void onClose()
{
_webSocket.onClose(WebSocketConnectionD06.CLOSE_NORMAL,"");
}
/* ------------------------------------------------------------ */
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)
code=WebSocketConnectionD06.CLOSE_NORMAL;
byte[] bytes = ("xx"+(message==null?"":message)).getBytes(StringUtil.__ISO_8859_1);
bytes[0]=(byte)(code/0x100);
bytes[1]=(byte)(code%0x100);
_generator.addFrame((byte)0x8,WebSocketConnectionD06.OP_CLOSE,bytes,0,bytes.length);
}
_generator.flush();
}
catch(IOException e)
{
LOG.ignore(e);
}
finally
{
_closedOut=true;
}
}
public void shutdown()
{
final WebSocket.Connection connection = _connection;
if (connection != null)
connection.close(CLOSE_SHUTDOWN, null);
}
/* ------------------------------------------------------------ */
public void fillBuffersFrom(ByteBuffer buffer)
{
_parser.fill(buffer);
}
/* ------------------------------------------------------------ */
private void checkWriteable()
{
if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint)
{
((AsyncEndPoint)_endp).scheduleWrite();
}
}
/* ------------------------------------------------------------ */
public List<Extension> getExtensions()
{
return Collections.emptyList();
}
protected void onFrameHandshake()
{
if (_onFrame!=null)
{
_onFrame.onHandshake(_connection);
}
}
protected void onWebSocketOpen()
{
_webSocket.onOpen(_connection);
}
/* ------------------------------------------------------------ */
private class FrameConnectionD06 implements WebSocket.FrameConnection
{
volatile boolean _disconnecting;
int _maxTextMessage=WebSocketConnectionD06.this._maxTextMessageSize;
int _maxBinaryMessage=WebSocketConnectionD06.this._maxBinaryMessageSize;
/* ------------------------------------------------------------ */
public synchronized void sendMessage(String content) throws IOException
{
if (_closedOut)
throw new IOException("closing");
byte[] data = content.getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,WebSocketConnectionD06.OP_TEXT,data,0,data.length);
_generator.flush();
checkWriteable();
}
/* ------------------------------------------------------------ */
public synchronized void sendMessage(byte[] content, int offset, int length) throws IOException
{
if (_closedOut)
throw new IOException("closing");
_generator.addFrame((byte)0x8,WebSocketConnectionD06.OP_BINARY,content,offset,length);
_generator.flush();
checkWriteable();
}
/* ------------------------------------------------------------ */
public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
{
if (_closedOut)
throw new IOException("closing");
_generator.addFrame(flags,opcode,content,offset,length);
_generator.flush();
checkWriteable();
}
/* ------------------------------------------------------------ */
public void sendControl(byte control, byte[] data, int offset, int length) throws IOException
{
if (_closedOut)
throw new IOException("closing");
_generator.addFrame((byte)0x8,control,data,offset,length);
_generator.flush();
checkWriteable();
}
/* ------------------------------------------------------------ */
public boolean isMessageComplete(byte flags)
{
return isLastFrame(flags);
}
/* ------------------------------------------------------------ */
public boolean isOpen()
{
return _endp!=null&&_endp.isOpen();
}
/* ------------------------------------------------------------ */
public void close(int code, String message)
{
if (_disconnecting)
return;
_disconnecting=true;
WebSocketConnectionD06.this.closeOut(code,message);
}
/* ------------------------------------------------------------ */
public void setMaxIdleTime(int ms)
{
try
{
_endp.setMaxIdleTime(ms);
}
catch(IOException e)
{
LOG.warn(e);
}
}
/* ------------------------------------------------------------ */
public void setMaxTextMessageSize(int size)
{
_maxTextMessage=size;
}
/* ------------------------------------------------------------ */
public void setMaxBinaryMessageSize(int size)
{
_maxBinaryMessage=size;
}
/* ------------------------------------------------------------ */
public int getMaxTextMessageSize()
{
return _maxTextMessage;
}
/* ------------------------------------------------------------ */
public int getMaxIdleTime()
{
return _endp.getMaxIdleTime();
}
/* ------------------------------------------------------------ */
public int getMaxBinaryMessageSize()
{
return _maxBinaryMessage;
}
/* ------------------------------------------------------------ */
public String getProtocol()
{
return _protocol;
}
/* ------------------------------------------------------------ */
public byte binaryOpcode()
{
return OP_BINARY;
}
/* ------------------------------------------------------------ */
public byte textOpcode()
{
return OP_TEXT;
}
/* ------------------------------------------------------------ */
public byte continuationOpcode()
{
return OP_CONTINUATION;
}
/* ------------------------------------------------------------ */
public byte finMask()
{
return 0x8;
}
/* ------------------------------------------------------------ */
public boolean isControl(byte opcode)
{
return isControlFrame(opcode);
}
/* ------------------------------------------------------------ */
public boolean isText(byte opcode)
{
return opcode==OP_TEXT;
}
/* ------------------------------------------------------------ */
public boolean isBinary(byte opcode)
{
return opcode==OP_BINARY;
}
/* ------------------------------------------------------------ */
public boolean isContinuation(byte opcode)
{
return opcode==OP_CONTINUATION;
}
/* ------------------------------------------------------------ */
public boolean isClose(byte opcode)
{
return opcode==OP_CLOSE;
}
/* ------------------------------------------------------------ */
public boolean isPing(byte opcode)
{
return opcode==OP_PING;
}
/* ------------------------------------------------------------ */
public boolean isPong(byte opcode)
{
return opcode==OP_PONG;
}
/* ------------------------------------------------------------ */
public void disconnect()
{
close();
}
/* ------------------------------------------------------------ */
public void close()
{
close(CLOSE_NORMAL,null);
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return this.getClass().getSimpleName()+"@"+_endp.getLocalAddr()+":"+_endp.getLocalPort()+"<->"+_endp.getRemoteAddr()+":"+_endp.getRemotePort();
}
public void setAllowFrameFragmentation(boolean allowFragmentation)
{
}
public boolean isAllowFrameFragmentation()
{
return false;
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class FrameHandlerD06 implements WebSocketParser.FrameHandler
{
private final Utf8StringBuilder _utf8 = new Utf8StringBuilder();
private ByteArrayBuffer _aggregate;
private byte _opcode=-1;
public void onFrame(byte flags, byte opcode, ByteBuffer buffer)
{
boolean lastFrame = isLastFrame(flags);
synchronized(WebSocketConnectionD06.this)
{
// Ignore incoming after a close
if (_closedIn)
return;
try
{
byte[] array=buffer.array();
// Deliver frame if websocket is a FrameWebSocket
if (_onFrame!=null)
{
if (_onFrame.onFrame(flags,opcode,array,buffer.getIndex(),buffer.length()))
return;
}
if (_onControl!=null && isControlFrame(opcode))
{
if (_onControl.onControl(opcode,array,buffer.getIndex(),buffer.length()))
return;
}
switch(opcode)
{
case WebSocketConnectionD06.OP_CONTINUATION:
{
// If text, append to the message buffer
if (_opcode==WebSocketConnectionD06.OP_TEXT && _connection.getMaxTextMessageSize()>=0)
{
if (_utf8.append(buffer.array(),buffer.getIndex(),buffer.length(),_connection.getMaxTextMessageSize()))
{
// If this is the last fragment, deliver the text buffer
if (lastFrame && _onTextMessage!=null)
{
_opcode=-1;
String msg =_utf8.toString();
_utf8.reset();
_onTextMessage.onMessage(msg);
}
}
else
{
_connection.close(WebSocketConnectionD06.CLOSE_LARGE,"Text message size > "+_connection.getMaxTextMessageSize()+" chars");
_utf8.reset();
_opcode=-1;
}
}
else if (_opcode>=0 && _connection.getMaxBinaryMessageSize()>=0)
{
if (_aggregate.space()<_aggregate.length())
{
_connection.close(WebSocketConnectionD06.CLOSE_LARGE,"Message size > "+_connection.getMaxBinaryMessageSize());
_aggregate.clear();
_opcode=-1;
}
else
{
_aggregate.put(buffer);
// If this is the last fragment, deliver
if (lastFrame && _onBinaryMessage!=null)
{
try
{
_onBinaryMessage.onMessage(_aggregate.array(),_aggregate.getIndex(),_aggregate.length());
}
finally
{
_opcode=-1;
_aggregate.clear();
}
}
}
}
break;
}
case WebSocketConnectionD06.OP_PING:
{
LOG.debug("PING {}",this);
if (!_closedOut)
_connection.sendControl(WebSocketConnectionD06.OP_PONG,buffer.array(),buffer.getIndex(),buffer.length());
break;
}
case WebSocketConnectionD06.OP_PONG:
{
LOG.debug("PONG {}",this);
break;
}
case WebSocketConnectionD06.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);
}
closeIn(code,message);
break;
}
case WebSocketConnectionD06.OP_TEXT:
{
if(_onTextMessage!=null)
{
if (lastFrame)
{
// Deliver the message
_onTextMessage.onMessage(buffer.toString(StringUtil.__UTF8));
}
else
{
if (_connection.getMaxTextMessageSize()>=0)
{
// If this is a text fragment, append to buffer
if (_utf8.append(buffer.array(),buffer.getIndex(),buffer.length(),_connection.getMaxTextMessageSize()))
_opcode=WebSocketConnectionD06.OP_TEXT;
else
{
_utf8.reset();
_opcode=-1;
_connection.close(WebSocketConnectionD06.CLOSE_LARGE,"Text message size > "+_connection.getMaxTextMessageSize()+" chars");
}
}
}
}
break;
}
default:
{
if (_onBinaryMessage!=null)
{
if (lastFrame)
{
_onBinaryMessage.onMessage(array,buffer.getIndex(),buffer.length());
}
else
{
if (_connection.getMaxBinaryMessageSize()>=0)
{
if (buffer.length()>_connection.getMaxBinaryMessageSize())
{
_connection.close(WebSocketConnectionD06.CLOSE_LARGE,"Message size > "+_connection.getMaxBinaryMessageSize());
if (_aggregate!=null)
_aggregate.clear();
_opcode=-1;
}
else
{
_opcode=opcode;
if (_aggregate==null)
_aggregate=new ByteArrayBuffer(_connection.getMaxBinaryMessageSize());
_aggregate.put(buffer);
}
}
}
}
}
}
}
catch(Throwable th)
{
LOG.warn(th);
}
}
}
public void close(int code,String message)
{
_connection.close(code,message);
}
@Override
public String toString()
{
return WebSocketConnectionD06.this.toString()+"FH";
}
}
/* ------------------------------------------------------------ */
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);
}
}
}

View File

@ -1,852 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.List;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
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;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.WebSocket.OnBinaryMessage;
import org.eclipse.jetty.websocket.WebSocket.OnControl;
import org.eclipse.jetty.websocket.WebSocket.OnFrame;
import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
public class WebSocketConnectionD08 extends AbstractConnection implements WebSocketConnection
{
private static final Logger LOG = Log.getLogger(WebSocketConnectionD08.class);
final static byte OP_CONTINUATION = 0x00;
final static byte OP_TEXT = 0x01;
final static byte OP_BINARY = 0x02;
final static byte OP_EXT_DATA = 0x03;
final static byte OP_CONTROL = 0x08;
final static byte OP_CLOSE = 0x08;
final static byte OP_PING = 0x09;
final static byte OP_PONG = 0x0A;
final static byte OP_EXT_CTRL = 0x0B;
final static int CLOSE_NORMAL=1000;
final static int CLOSE_SHUTDOWN=1001;
final static int CLOSE_PROTOCOL=1002;
final static int CLOSE_BADDATA=1003;
final static int CLOSE_NOCODE=1005;
final static int CLOSE_NOCLOSE=1006;
final static int CLOSE_NOTUTF8=1007;
final static int FLAG_FIN=0x8;
final static int VERSION=8;
static boolean isLastFrame(byte flags)
{
return (flags&FLAG_FIN)!=0;
}
static boolean isControlFrame(byte opcode)
{
return (opcode&OP_CONTROL)!=0;
}
private final static byte[] MAGIC;
private final List<Extension> _extensions;
private final WebSocketParserD08 _parser;
private final WebSocketParser.FrameHandler _inbound;
private final WebSocketGeneratorD08 _generator;
private final WebSocketGenerator _outbound;
private final WebSocket _webSocket;
private final OnFrame _onFrame;
private final OnBinaryMessage _onBinaryMessage;
private final OnTextMessage _onTextMessage;
private final OnControl _onControl;
private final String _protocol;
private final int _draft;
private final ClassLoader _context;
private volatile int _closeCode;
private volatile String _closeMessage;
private volatile boolean _closedIn;
private volatile boolean _closedOut;
private int _maxTextMessageSize=-1;
private int _maxBinaryMessageSize=-1;
static
{
try
{
MAGIC="258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(StringUtil.__ISO_8859_1);
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
}
private final WebSocketParser.FrameHandler _frameHandler= new WSFrameHandler();
private final WebSocket.FrameConnection _connection = new WSFrameConnection();
/* ------------------------------------------------------------ */
public WebSocketConnectionD08(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List<Extension> extensions,int draft)
throws IOException
{
this(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft,null);
}
/* ------------------------------------------------------------ */
public WebSocketConnectionD08(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List<Extension> extensions,int draft, MaskGen maskgen)
throws IOException
{
super(endpoint,timestamp);
_context=Thread.currentThread().getContextClassLoader();
_draft=draft;
_endp.setMaxIdleTime(maxIdleTime);
_webSocket = websocket;
_onFrame=_webSocket instanceof OnFrame ? (OnFrame)_webSocket : null;
_onTextMessage=_webSocket instanceof OnTextMessage ? (OnTextMessage)_webSocket : null;
_onBinaryMessage=_webSocket instanceof OnBinaryMessage ? (OnBinaryMessage)_webSocket : null;
_onControl=_webSocket instanceof OnControl ? (OnControl)_webSocket : null;
_generator = new WebSocketGeneratorD08(buffers, _endp,maskgen);
_extensions=extensions;
if (_extensions!=null)
{
int e=0;
for (Extension extension : _extensions)
{
extension.bind(
_connection,
e==extensions.size()-1?_frameHandler:extensions.get(e+1),
e==0?_generator:extensions.get(e-1));
e++;
}
}
_outbound=(_extensions==null||_extensions.size()==0)?_generator:extensions.get(extensions.size()-1);
_inbound=(_extensions==null||_extensions.size()==0)?_frameHandler:extensions.get(0);
_parser = new WebSocketParserD08(buffers, endpoint,_inbound,maskgen==null);
_protocol=protocol;
}
/* ------------------------------------------------------------ */
public WebSocket.Connection getConnection()
{
return _connection;
}
/* ------------------------------------------------------------ */
public List<Extension> getExtensions()
{
if (_extensions==null)
return Collections.emptyList();
return _extensions;
}
/* ------------------------------------------------------------ */
public Connection handle() throws IOException
{
Thread current = Thread.currentThread();
ClassLoader oldcontext = current.getContextClassLoader();
current.setContextClassLoader(_context);
try
{
// handle the framing protocol
boolean progress=true;
while (progress)
{
int flushed=_generator.flushBuffer();
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
{
current.setContextClassLoader(oldcontext);
_parser.returnBuffer();
_generator.returnBuffer();
if (_endp.isOpen())
{
if (_closedIn && _closedOut && _outbound.isBufferEmpty())
_endp.close();
else if (_endp.isInputShutdown() && !_closedIn)
closeIn(CLOSE_NOCLOSE,null);
else
checkWriteable();
}
}
return this;
}
/* ------------------------------------------------------------ */
public void onInputShutdown() throws IOException
{
// TODO
}
/* ------------------------------------------------------------ */
public boolean isIdle()
{
return _parser.isBufferEmpty() && _outbound.isBufferEmpty();
}
/* ------------------------------------------------------------ */
@Override
public void onIdleExpired(long idleForMs)
{
closeOut(WebSocketConnectionD08.CLOSE_NORMAL,"Idle for "+idleForMs+"ms > "+_endp.getMaxIdleTime()+"ms");
}
/* ------------------------------------------------------------ */
public boolean isSuspended()
{
return false;
}
/* ------------------------------------------------------------ */
public void onClose()
{
final boolean closed;
synchronized (this)
{
closed=_closeCode==0;
if (closed)
_closeCode=WebSocketConnectionD08.CLOSE_NOCLOSE;
}
if (closed)
_webSocket.onClose(WebSocketConnectionD08.CLOSE_NOCLOSE,"closed");
}
/* ------------------------------------------------------------ */
public void closeIn(int code,String message)
{
LOG.debug("ClosedIn {} {}",this,message);
final boolean closedOut;
final boolean closed;
synchronized (this)
{
closedOut=_closedOut;
_closedIn=true;
closed=_closeCode==0;
if (closed)
{
_closeCode=code;
_closeMessage=message;
}
}
try
{
if (closed)
_webSocket.onClose(code,message);
}
finally
{
try
{
if (closedOut)
_endp.close();
else
closeOut(code,message);
}
catch(IOException e)
{
LOG.ignore(e);
}
}
}
/* ------------------------------------------------------------ */
public void closeOut(int code,String message)
{
LOG.debug("ClosedOut {} {}",this,message);
final boolean close;
final boolean closed;
synchronized (this)
{
close=_closedIn || _closedOut;
_closedOut=true;
closed=_closeCode==0;
if (closed)
{
_closeCode=code;
_closeMessage=message;
}
}
try
{
if (closed)
_webSocket.onClose(code,message);
}
finally
{
try
{
if (close)
_endp.close();
else
{
if (code<=0)
code=WebSocketConnectionD08.CLOSE_NORMAL;
byte[] bytes = ("xx"+(message==null?"":message)).getBytes(StringUtil.__ISO_8859_1);
bytes[0]=(byte)(code/0x100);
bytes[1]=(byte)(code%0x100);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD08.OP_CLOSE,bytes,0,bytes.length);
}
_outbound.flush();
}
catch(IOException e)
{
LOG.ignore(e);
}
}
}
public void shutdown()
{
final WebSocket.Connection connection = _connection;
if (connection != null)
connection.close(CLOSE_SHUTDOWN, null);
}
/* ------------------------------------------------------------ */
public void fillBuffersFrom(ByteBuffer buffer)
{
_parser.fill(buffer);
}
/* ------------------------------------------------------------ */
private void checkWriteable()
{
if (!_outbound.isBufferEmpty() && _endp instanceof AsyncEndPoint)
{
((AsyncEndPoint)_endp).scheduleWrite();
}
}
protected void onFrameHandshake()
{
if (_onFrame != null)
{
_onFrame.onHandshake(_connection);
}
}
protected void onWebSocketOpen()
{
_webSocket.onOpen(_connection);
}
/* ------------------------------------------------------------ */
private class WSFrameConnection implements WebSocket.FrameConnection
{
volatile boolean _disconnecting;
/* ------------------------------------------------------------ */
public void sendMessage(String content) throws IOException
{
if (_closedOut)
throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
byte[] data = content.getBytes(StringUtil.__UTF8);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD08.OP_TEXT,data,0,data.length);
checkWriteable();
}
/* ------------------------------------------------------------ */
public void sendMessage(byte[] content, int offset, int length) throws IOException
{
if (_closedOut)
throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD08.OP_BINARY,content,offset,length);
checkWriteable();
}
/* ------------------------------------------------------------ */
public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
{
if (_closedOut)
throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
_outbound.addFrame(flags,opcode,content,offset,length);
checkWriteable();
}
/* ------------------------------------------------------------ */
public void sendControl(byte ctrl, byte[] data, int offset, int length) throws IOException
{
if (_closedOut)
throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
_outbound.addFrame((byte)FLAG_FIN,ctrl,data,offset,length);
checkWriteable();
}
/* ------------------------------------------------------------ */
public boolean isMessageComplete(byte flags)
{
return isLastFrame(flags);
}
/* ------------------------------------------------------------ */
public boolean isOpen()
{
return _endp!=null&&_endp.isOpen();
}
/* ------------------------------------------------------------ */
public void close(int code, String message)
{
if (_disconnecting)
return;
_disconnecting=true;
WebSocketConnectionD08.this.closeOut(code,message);
}
/* ------------------------------------------------------------ */
public void setMaxIdleTime(int ms)
{
try
{
_endp.setMaxIdleTime(ms);
}
catch(IOException e)
{
LOG.warn(e);
}
}
/* ------------------------------------------------------------ */
public void setMaxTextMessageSize(int size)
{
_maxTextMessageSize=size;
}
/* ------------------------------------------------------------ */
public void setMaxBinaryMessageSize(int size)
{
_maxBinaryMessageSize=size;
}
/* ------------------------------------------------------------ */
public int getMaxIdleTime()
{
return _endp.getMaxIdleTime();
}
/* ------------------------------------------------------------ */
public int getMaxTextMessageSize()
{
return _maxTextMessageSize;
}
/* ------------------------------------------------------------ */
public int getMaxBinaryMessageSize()
{
return _maxBinaryMessageSize;
}
/* ------------------------------------------------------------ */
public String getProtocol()
{
return _protocol;
}
/* ------------------------------------------------------------ */
public byte binaryOpcode()
{
return OP_BINARY;
}
/* ------------------------------------------------------------ */
public byte textOpcode()
{
return OP_TEXT;
}
/* ------------------------------------------------------------ */
public byte continuationOpcode()
{
return OP_CONTINUATION;
}
/* ------------------------------------------------------------ */
public byte finMask()
{
return FLAG_FIN;
}
/* ------------------------------------------------------------ */
public boolean isControl(byte opcode)
{
return isControlFrame(opcode);
}
/* ------------------------------------------------------------ */
public boolean isText(byte opcode)
{
return opcode==OP_TEXT;
}
/* ------------------------------------------------------------ */
public boolean isBinary(byte opcode)
{
return opcode==OP_BINARY;
}
/* ------------------------------------------------------------ */
public boolean isContinuation(byte opcode)
{
return opcode==OP_CONTINUATION;
}
/* ------------------------------------------------------------ */
public boolean isClose(byte opcode)
{
return opcode==OP_CLOSE;
}
/* ------------------------------------------------------------ */
public boolean isPing(byte opcode)
{
return opcode==OP_PING;
}
/* ------------------------------------------------------------ */
public boolean isPong(byte opcode)
{
return opcode==OP_PONG;
}
/* ------------------------------------------------------------ */
public void disconnect()
{
close();
}
/* ------------------------------------------------------------ */
public void close()
{
close(CLOSE_NORMAL,null);
}
/* ------------------------------------------------------------ */
public void setAllowFrameFragmentation(boolean allowFragmentation)
{
_parser.setFakeFragments(allowFragmentation);
}
/* ------------------------------------------------------------ */
public boolean isAllowFrameFragmentation()
{
return _parser.isFakeFragments();
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return this.getClass().getSimpleName()+"D08@"+_endp.getLocalAddr()+":"+_endp.getLocalPort()+"<->"+_endp.getRemoteAddr()+":"+_endp.getRemotePort();
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class WSFrameHandler implements WebSocketParser.FrameHandler
{
private final Utf8StringBuilder _utf8 = new Utf8StringBuilder();
private ByteArrayBuffer _aggregate;
private byte _opcode=-1;
public void onFrame(final byte flags, final byte opcode, final ByteBuffer buffer)
{
boolean lastFrame = isLastFrame(flags);
synchronized(WebSocketConnectionD08.this)
{
// Ignore incoming after a close
if (_closedIn)
return;
}
try
{
byte[] array=buffer.array();
// Deliver frame if websocket is a FrameWebSocket
if (_onFrame!=null)
{
if (_onFrame.onFrame(flags,opcode,array,buffer.getIndex(),buffer.length()))
return;
}
if (_onControl!=null && isControlFrame(opcode))
{
if (_onControl.onControl(opcode,array,buffer.getIndex(),buffer.length()))
return;
}
switch(opcode)
{
case WebSocketConnectionD08.OP_CONTINUATION:
{
// If text, append to the message buffer
if (_onTextMessage!=null && _opcode==WebSocketConnectionD08.OP_TEXT)
{
if (_utf8.append(buffer.array(),buffer.getIndex(),buffer.length(),_connection.getMaxTextMessageSize()))
{
// If this is the last fragment, deliver the text buffer
if (lastFrame)
{
_opcode=-1;
String msg =_utf8.toString();
_utf8.reset();
_onTextMessage.onMessage(msg);
}
}
else
textMessageTooLarge();
}
if (_opcode>=0 && _connection.getMaxBinaryMessageSize()>=0)
{
if (checkBinaryMessageSize(_aggregate.length(),buffer.length()))
{
_aggregate.put(buffer);
// If this is the last fragment, deliver
if (lastFrame && _onBinaryMessage!=null)
{
try
{
_onBinaryMessage.onMessage(_aggregate.array(),_aggregate.getIndex(),_aggregate.length());
}
finally
{
_opcode=-1;
_aggregate.clear();
}
}
}
}
break;
}
case WebSocketConnectionD08.OP_PING:
{
LOG.debug("PING {}",this);
if (!_closedOut)
_connection.sendControl(WebSocketConnectionD08.OP_PONG,buffer.array(),buffer.getIndex(),buffer.length());
break;
}
case WebSocketConnectionD08.OP_PONG:
{
LOG.debug("PONG {}",this);
break;
}
case WebSocketConnectionD08.OP_CLOSE:
{
int code=WebSocketConnectionD08.CLOSE_NOCODE;
String message=null;
if (buffer.length()>=2)
{
code=buffer.array()[buffer.getIndex()]*0x100+buffer.array()[buffer.getIndex()+1];
if (buffer.length()>2)
message=new String(buffer.array(),buffer.getIndex()+2,buffer.length()-2,StringUtil.__UTF8);
}
closeIn(code,message);
break;
}
case WebSocketConnectionD08.OP_TEXT:
{
if(_onTextMessage!=null)
{
if (_connection.getMaxTextMessageSize()<=0)
{
// No size limit, so handle only final frames
if (lastFrame)
_onTextMessage.onMessage(buffer.toString(StringUtil.__UTF8));
else
{
LOG.warn("Frame discarded. Text aggregation disabled for {}",_endp);
_connection.close(WebSocketConnectionD08.CLOSE_BADDATA,"Text frame aggregation disabled");
}
}
// append bytes to message buffer (if they fit)
else if (_utf8.append(buffer.array(),buffer.getIndex(),buffer.length(),_connection.getMaxTextMessageSize()))
{
if (lastFrame)
{
String msg =_utf8.toString();
_utf8.reset();
_onTextMessage.onMessage(msg);
}
else
{
_opcode=WebSocketConnectionD08.OP_TEXT;
}
}
else
textMessageTooLarge();
}
break;
}
default:
{
if (_onBinaryMessage!=null && checkBinaryMessageSize(0,buffer.length()))
{
if (lastFrame)
{
_onBinaryMessage.onMessage(array,buffer.getIndex(),buffer.length());
}
else if (_connection.getMaxBinaryMessageSize()>=0)
{
_opcode=opcode;
if (_aggregate==null)
_aggregate=new ByteArrayBuffer(_connection.getMaxBinaryMessageSize());
_aggregate.put(buffer);
}
else
{
LOG.warn("Frame discarded. Binary aggregation disabed for {}",_endp);
_connection.close(WebSocketConnectionD08.CLOSE_BADDATA,"Binary frame aggregation disabled");
}
}
}
}
}
catch(Throwable th)
{
LOG.warn(th);
}
}
private boolean checkBinaryMessageSize(int bufferLen, int length)
{
int max = _connection.getMaxBinaryMessageSize();
if (max>0 && (bufferLen+length)>max)
{
LOG.warn("Binary message too large > {}B for {}",_connection.getMaxBinaryMessageSize(),_endp);
_connection.close(WebSocketConnectionD08.CLOSE_BADDATA,"Message size > "+_connection.getMaxBinaryMessageSize());
_opcode=-1;
if (_aggregate!=null)
_aggregate.clear();
return false;
}
return true;
}
private void textMessageTooLarge()
{
LOG.warn("Text message too large > {} chars for {}",_connection.getMaxTextMessageSize(),_endp);
_connection.close(WebSocketConnectionD08.CLOSE_BADDATA,"Text message size > "+_connection.getMaxTextMessageSize()+" chars");
_opcode=-1;
_utf8.reset();
}
public void close(int code,String message)
{
if (code!=CLOSE_NORMAL)
LOG.warn("Close: "+code+" "+message);
_connection.close(code,message);
}
@Override
public String toString()
{
return WebSocketConnectionD08.this.toString()+"FH";
}
}
/* ------------------------------------------------------------ */
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);
}
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return String.format("WS/D%d p=%s g=%s", _draft, _parser, _generator);
}
}

View File

@ -39,11 +39,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpException;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.io.ConnectedEndPoint;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.BlockingHttpConnection;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
@ -239,29 +235,6 @@ public class WebSocketFactory extends AbstractLifeCycle
final WebSocketServletConnection connection;
switch (draft)
{
case -1: // unspecified draft/version
case 0: // Old school draft/version
{
connection = new WebSocketServletConnectionD00(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
break;
}
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
{
connection = new WebSocketServletConnectionD06(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
break;
}
case 7:
case 8:
{
List<Extension> extensions = initExtensions(extensions_requested, 8 - WebSocketConnectionD08.OP_EXT_DATA, 16 - WebSocketConnectionD08.OP_EXT_CTRL, 3);
connection = new WebSocketServletConnectionD08(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol, extensions, draft);
break;
}
case WebSocketConnectionRFC6455.VERSION: // RFC 6455 Version
{
List<Extension> extensions = initExtensions(extensions_requested, 8 - WebSocketConnectionRFC6455.OP_EXT_DATA, 16 - WebSocketConnectionRFC6455.OP_EXT_CTRL, 3);

View File

@ -1,183 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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.math.BigInteger;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
/* ------------------------------------------------------------ */
/** WebSocketGenerator.
* This class generates websocket packets.
* It is fully synchronized because it is likely that async
* threads will call the addMessage methods while other
* threads are flushing the generator.
*/
public class WebSocketGeneratorD00 implements WebSocketGenerator
{
final private WebSocketBuffers _buffers;
final private EndPoint _endp;
private ByteBuffer _buffer;
public WebSocketGeneratorD00(WebSocketBuffers buffers, EndPoint endp)
{
_buffers=buffers;
_endp=endp;
}
public synchronized void addFrame(byte flags, byte opcode,byte[] content, int offset, int length) throws IOException
{
long blockFor=_endp.getMaxIdleTime();
if (_buffer==null)
_buffer=_buffers.getDirectBuffer();
if (_buffer.space() == 0)
expelBuffer(blockFor);
bufferPut(opcode, blockFor);
if (isLengthFrame(opcode))
{
// Send a length delimited frame
// How many bytes we need for the length ?
// We have 7 bits available, so log2(length) / 7 + 1
// For example, 50000 bytes is 2 8-bytes: 11000011 01010000
// but we need to write it in 3 7-bytes 0000011 0000110 1010000
// 65536 == 1 00000000 00000000 => 100 0000000 0000000
int lengthBytes = new BigInteger(String.valueOf(length)).bitLength() / 7 + 1;
for (int i = lengthBytes - 1; i > 0; --i)
{
byte lengthByte = (byte)(0x80 | (0x7F & (length >> 7 * i)));
bufferPut(lengthByte, blockFor);
}
bufferPut((byte)(0x7F & length), blockFor);
}
int remaining = length;
while (remaining > 0)
{
int chunk = remaining < _buffer.space() ? remaining : _buffer.space();
_buffer.put(content, offset + (length - remaining), chunk);
remaining -= chunk;
if (_buffer.space() > 0)
{
if (!isLengthFrame(opcode))
_buffer.put((byte)0xFF);
// Gently flush the data, issuing a non-blocking write
flushBuffer();
}
else
{
// Forcibly flush the data, issuing a blocking write
expelBuffer(blockFor);
if (remaining == 0)
{
if (!isLengthFrame(opcode))
_buffer.put((byte)0xFF);
// Gently flush the data, issuing a non-blocking write
flushBuffer();
}
}
}
}
private synchronized boolean isLengthFrame(byte frame)
{
return (frame & WebSocketConnectionD00.LENGTH_FRAME) == WebSocketConnectionD00.LENGTH_FRAME;
}
private synchronized void bufferPut(byte datum, long blockFor) throws IOException
{
if (_buffer==null)
_buffer=_buffers.getDirectBuffer();
_buffer.put(datum);
if (_buffer.space() == 0)
expelBuffer(blockFor);
}
public synchronized int flush(int blockFor) throws IOException
{
return expelBuffer(blockFor);
}
public synchronized int flush() throws IOException
{
int flushed = flushBuffer();
if (_buffer!=null && _buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
return flushed;
}
private synchronized int flushBuffer() throws IOException
{
if (!_endp.isOpen())
throw new EofException();
if (_buffer!=null && _buffer.hasContent())
return _endp.flush(_buffer);
return 0;
}
private synchronized int expelBuffer(long blockFor) throws IOException
{
if (_buffer==null)
return 0;
int result = flushBuffer();
_buffer.compact();
if (!_endp.isBlocking())
{
while (_buffer.space()==0)
{
boolean ready = _endp.blockWritable(blockFor);
if (!ready)
throw new IOException("Write timeout");
result += flushBuffer();
_buffer.compact();
}
}
return result;
}
public synchronized boolean isBufferEmpty()
{
return _buffer==null || _buffer.length()==0;
}
}

View File

@ -1,244 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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 org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
/* ------------------------------------------------------------ */
/** WebSocketGenerator.
* This class generates websocket packets.
* It is fully synchronized because it is likely that async
* threads will call the addMessage methods while other
* threads are flushing the generator.
*/
public class WebSocketGeneratorD06 implements WebSocketGenerator
{
final private WebSocketBuffers _buffers;
final private EndPoint _endp;
private ByteBuffer _buffer;
private final byte[] _mask=new byte[4];
private int _m;
private boolean _opsent;
private final MaskGen _maskGen;
public WebSocketGeneratorD06(WebSocketBuffers buffers, EndPoint endp)
{
_buffers=buffers;
_endp=endp;
_maskGen=null;
}
public WebSocketGeneratorD06(WebSocketBuffers buffers, EndPoint endp, MaskGen maskGen)
{
_buffers=buffers;
_endp=endp;
_maskGen=maskGen;
}
public synchronized void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException
{
// System.err.printf("<< %s %s %s\n",TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),length);
long blockFor=_endp.getMaxIdleTime();
if (_buffer==null)
_buffer=(_maskGen!=null)?_buffers.getBuffer():_buffers.getDirectBuffer();
boolean last=WebSocketConnectionD06.isLastFrame(flags);
opcode=(byte)(((0xf&flags)<<4)+0xf&opcode);
int space=(_maskGen!=null)?14:10;
do
{
opcode = _opsent?WebSocketConnectionD06.OP_CONTINUATION:opcode;
_opsent=true;
int payload=length;
if (payload+space>_buffer.capacity())
{
// We must fragement, so clear FIN bit
opcode&=(byte)0x7F; // Clear the FIN bit
payload=_buffer.capacity()-space;
}
else if (last)
opcode|=(byte)0x80; // Set the FIN bit
// ensure there is space for header
if (_buffer.space() <= space)
expelBuffer(blockFor);
// write mask
if ((_maskGen!=null))
{
_maskGen.genMask(_mask);
_m=0;
_buffer.put(_mask);
}
// write the opcode and length
if (payload>0xffff)
{
bufferPut(new byte[]{
opcode,
(byte)0x7f,
(byte)0,
(byte)0,
(byte)0,
(byte)0,
(byte)((payload>>24)&0xff),
(byte)((payload>>16)&0xff),
(byte)((payload>>8)&0xff),
(byte)(payload&0xff)});
}
else if (payload >=0x7e)
{
bufferPut(new byte[]{
opcode,
(byte)0x7e,
(byte)(payload>>8),
(byte)(payload&0xff)});
}
else
{
bufferPut(opcode);
bufferPut((byte)payload);
}
// write payload
int remaining = payload;
while (remaining > 0)
{
_buffer.compact();
int chunk = remaining < _buffer.space() ? remaining : _buffer.space();
if ((_maskGen!=null))
{
for (int i=0;i<chunk;i++)
bufferPut(content[offset+ (payload-remaining)+i]);
}
else
_buffer.put(content, offset + (payload - remaining), chunk);
remaining -= chunk;
if (_buffer.space() > 0)
{
// Gently flush the data, issuing a non-blocking write
flushBuffer();
}
else
{
// Forcibly flush the data, issuing a blocking write
expelBuffer(blockFor);
if (remaining == 0)
{
// Gently flush the data, issuing a non-blocking write
flushBuffer();
}
}
}
offset+=payload;
length-=payload;
}
while (length>0);
_opsent=!last;
}
private synchronized void bufferPut(byte[] data) throws IOException
{
if (_maskGen!=null)
for (int i=0;i<data.length;i++)
data[i]^=_mask[+_m++%4];
_buffer.put(data);
}
private synchronized void bufferPut(byte data) throws IOException
{
_buffer.put((byte)(data^_mask[+_m++%4]));
}
public synchronized int flush(int blockFor) throws IOException
{
return expelBuffer(blockFor);
}
public synchronized int flush() throws IOException
{
int flushed = flushBuffer();
if (_buffer!=null && _buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
return flushed;
}
private synchronized int flushBuffer() throws IOException
{
if (!_endp.isOpen())
throw new EofException();
if (_buffer!=null)
return _endp.flush(_buffer);
return 0;
}
private synchronized int expelBuffer(long blockFor) throws IOException
{
if (_buffer==null)
return 0;
int result = flushBuffer();
_buffer.compact();
if (!_endp.isBlocking())
{
while (_buffer.space()==0)
{
boolean ready = _endp.blockWritable(blockFor);
if (!ready)
throw new IOException("Write timeout");
result += flushBuffer();
_buffer.compact();
}
}
return result;
}
public synchronized boolean isBufferEmpty()
{
return _buffer==null || _buffer.length()==0;
}
}

View File

@ -1,248 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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 org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
/* ------------------------------------------------------------ */
/** WebSocketGenerator.
* This class generates websocket packets.
* It is fully synchronized because it is likely that async
* threads will call the addMessage methods while other
* threads are flushing the generator.
*/
public class WebSocketGeneratorD08 implements WebSocketGenerator
{
final private WebSocketBuffers _buffers;
final private EndPoint _endp;
private ByteBuffer _buffer;
private final byte[] _mask=new byte[4];
private int _m;
private boolean _opsent;
private final MaskGen _maskGen;
public WebSocketGeneratorD08(WebSocketBuffers buffers, EndPoint endp)
{
_buffers=buffers;
_endp=endp;
_maskGen=null;
}
public WebSocketGeneratorD08(WebSocketBuffers buffers, EndPoint endp, MaskGen maskGen)
{
_buffers=buffers;
_endp=endp;
_maskGen=maskGen;
}
public synchronized ByteBuffer getBuffer()
{
return _buffer;
}
public synchronized void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException
{
// System.err.printf("<< %s %s %s\n",TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),length);
boolean mask=_maskGen!=null;
if (_buffer==null)
_buffer=mask?_buffers.getBuffer():_buffers.getDirectBuffer();
boolean last=WebSocketConnectionD08.isLastFrame(flags);
int space=mask?14:10;
do
{
opcode = _opsent?WebSocketConnectionD08.OP_CONTINUATION:opcode;
opcode=(byte)(((0xf&flags)<<4)+(0xf&opcode));
_opsent=true;
int payload=length;
if (payload+space>_buffer.capacity())
{
// We must fragement, so clear FIN bit
opcode=(byte)(opcode&0x7F); // Clear the FIN bit
payload=_buffer.capacity()-space;
}
else if (last)
opcode= (byte)(opcode|0x80); // Set the FIN bit
// ensure there is space for header
if (_buffer.space() <= space)
{
flushBuffer();
if (_buffer.space() <= space)
flush();
}
// write the opcode and length
if (payload>0xffff)
{
_buffer.put(new byte[]{
opcode,
mask?(byte)0xff:(byte)0x7f,
(byte)0,
(byte)0,
(byte)0,
(byte)0,
(byte)((payload>>24)&0xff),
(byte)((payload>>16)&0xff),
(byte)((payload>>8)&0xff),
(byte)(payload&0xff)});
}
else if (payload >=0x7e)
{
_buffer.put(new byte[]{
opcode,
mask?(byte)0xfe:(byte)0x7e,
(byte)(payload>>8),
(byte)(payload&0xff)});
}
else
{
_buffer.put(new byte[]{
opcode,
(byte)(mask?(0x80|payload):payload)});
}
// write mask
if (mask)
{
_maskGen.genMask(_mask);
_m=0;
_buffer.put(_mask);
}
// write payload
int remaining = payload;
while (remaining > 0)
{
_buffer.compact();
int chunk = remaining < _buffer.space() ? remaining : _buffer.space();
if (mask)
{
for (int i=0;i<chunk;i++)
_buffer.put((byte)(content[offset+ (payload-remaining)+i]^_mask[+_m++%4]));
}
else
_buffer.put(content, offset + (payload - remaining), chunk);
remaining -= chunk;
if (_buffer.space() > 0)
{
// Gently flush the data, issuing a non-blocking write
flushBuffer();
}
else
{
// Forcibly flush the data, issuing a blocking write
flush();
if (remaining == 0)
{
// Gently flush the data, issuing a non-blocking write
flushBuffer();
}
}
}
offset+=payload;
length-=payload;
}
while (length>0);
_opsent=!last;
if (_buffer!=null && _buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
}
public synchronized int flushBuffer() throws IOException
{
if (!_endp.isOpen())
throw new EofException();
if (_buffer!=null)
return _endp.flush(_buffer);
return 0;
}
public synchronized int flush() throws IOException
{
if (_buffer==null)
return 0;
int result = flushBuffer();
if (!_endp.isBlocking())
{
long now = System.currentTimeMillis();
long end=now+_endp.getMaxIdleTime();
while (_buffer.length()>0)
{
boolean ready = _endp.blockWritable(end-now);
if (!ready)
{
now = System.currentTimeMillis();
if (now<end)
continue;
throw new IOException("Write timeout");
}
result += flushBuffer();
}
}
_buffer.compact();
return result;
}
public synchronized boolean isBufferEmpty()
{
return _buffer==null || _buffer.length()==0;
}
public synchronized void returnBuffer()
{
if (_buffer!=null && _buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
}
}

View File

@ -29,6 +29,7 @@
package org.eclipse.jetty.websocket;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.io.EndPoint;

View File

@ -28,6 +28,8 @@
package org.eclipse.jetty.websocket;
import java.nio.ByteBuffer;

View File

@ -1,222 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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 org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* Parser the WebSocket protocol.
*
*/
public class WebSocketParserD00 implements WebSocketParser
{
private static final Logger LOG = Log.getLogger(WebSocketParserD00.class);
public static final int STATE_START=0;
public static final int STATE_SENTINEL_DATA=1;
public static final int STATE_LENGTH=2;
public static final int STATE_DATA=3;
private final WebSocketBuffers _buffers;
private final EndPoint _endp;
private final FrameHandler _handler;
private int _state;
private ByteBuffer _buffer;
private byte _opcode;
private int _length;
/* ------------------------------------------------------------ */
/**
* @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used.
* This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
* is mostly used.
* @param endp the endpoint
* @param handler the handler to notify when a parse event occurs
*/
public WebSocketParserD00(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler)
{
_buffers=buffers;
_endp=endp;
_handler=handler;
}
/* ------------------------------------------------------------ */
public boolean isBufferEmpty()
{
return _buffer==null || _buffer.length()==0;
}
/* ------------------------------------------------------------ */
public ByteBuffer getBuffer()
{
return _buffer;
}
/* ------------------------------------------------------------ */
/** Parse to next event.
* Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
* available. Fill data from the {@link EndPoint} only as necessary.
* @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
* that no bytes were read and no messages parsed. A positive number indicates either
* the bytes filled or the messages parsed.
*/
public int parseNext()
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
int progress=0;
// Loop until an datagram call back or can't fill anymore
while(true)
{
int length=_buffer.length();
// Fill buffer if we need a byte or need length
if (length == 0 || _state==STATE_DATA && length<_length)
{
// compact to mark (set at start of data)
_buffer.compact();
// if no space, then the data is too big for buffer
if (_buffer.space() == 0)
throw new IllegalStateException("FULL");
// catch IOExceptions (probably EOF) and try to parse what we have
try
{
int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
if (filled<=0)
return progress;
progress+=filled;
length=_buffer.length();
}
catch(IOException e)
{
LOG.debug(e);
return progress>0?progress:-1;
}
}
// Parse the buffer byte by byte (unless it is STATE_DATA)
byte b;
charloop: while (length-->0)
{
switch (_state)
{
case STATE_START:
b=_buffer.get();
_opcode=b;
if (_opcode<0)
{
_length=0;
_state=STATE_LENGTH;
}
else
{
_state=STATE_SENTINEL_DATA;
_buffer.mark(0);
}
continue;
case STATE_SENTINEL_DATA:
b=_buffer.get();
if ((b&0xff)==0xff)
{
_state=STATE_START;
int l=_buffer.getIndex()-_buffer.markIndex()-1;
progress++;
_handler.onFrame((byte)0,_opcode,_buffer.sliceFromMark(l));
_buffer.setMarkIndex(-1);
if (_buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
return progress;
}
continue;
case STATE_LENGTH:
b=_buffer.get();
_length=_length<<7 | (0x7f&b);
if (b>=0)
{
_state=STATE_DATA;
_buffer.mark(0);
}
continue;
case STATE_DATA:
if (_buffer.markIndex()<0)
if (_buffer.length()<_length)
break charloop;
ByteBuffer data=_buffer.sliceFromMark(_length);
_buffer.skip(_length);
_state=STATE_START;
progress++;
_handler.onFrame((byte)0, _opcode, data);
if (_buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
return progress;
}
}
}
}
/* ------------------------------------------------------------ */
public void fill(ByteBuffer buffer)
{
if (buffer!=null && buffer.length()>0)
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
_buffer.put(buffer);
buffer.clear();
}
}
}

View File

@ -1,320 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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 org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* Parser the WebSocket protocol.
*
*/
public class WebSocketParserD06 implements WebSocketParser
{
private static final Logger LOG = Log.getLogger(WebSocketParserD06.class);
public enum State {
START(0), MASK(4), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), DATA(0), SKIP(1);
int _needs;
State(int needs)
{
_needs=needs;
}
int getNeeds()
{
return _needs;
}
}
private final WebSocketBuffers _buffers;
private final EndPoint _endp;
private final FrameHandler _handler;
private final boolean _masked;
private State _state;
private ByteBuffer _buffer;
private byte _flags;
private byte _opcode;
private int _bytesNeeded;
private long _length;
private final byte[] _mask = new byte[4];
private int _m;
/* ------------------------------------------------------------ */
/**
* @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used.
* This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
* is mostly used.
* @param endp the endpoint
* @param handler the handler to notify when a parse event occurs
* @param masked whether masking should be handled
*/
public WebSocketParserD06(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean masked)
{
_buffers=buffers;
_endp=endp;
_handler=handler;
_masked=masked;
_state=State.START;
}
/* ------------------------------------------------------------ */
public boolean isBufferEmpty()
{
return _buffer==null || _buffer.length()==0;
}
/* ------------------------------------------------------------ */
public ByteBuffer getBuffer()
{
return _buffer;
}
/* ------------------------------------------------------------ */
/** Parse to next event.
* Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
* available. Fill data from the {@link EndPoint} only as necessary.
* @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
* that no bytes were read and no messages parsed. A positive number indicates either
* the bytes filled or the messages parsed.
*/
public int parseNext()
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
int total_filled=0;
int events=0;
// Loop until an datagram call back or can't fill anymore
while(true)
{
int available=_buffer.length();
// Fill buffer if we need a byte or need length
while (available<(_state==State.SKIP?1:_bytesNeeded))
{
// compact to mark (set at start of data)
_buffer.compact();
// if no space, then the data is too big for buffer
if (_buffer.space() == 0)
throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity());
// catch IOExceptions (probably EOF) and try to parse what we have
try
{
int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
if (filled<=0)
return (total_filled+events)>0?(total_filled+events):filled;
total_filled+=filled;
available=_buffer.length();
}
catch(IOException e)
{
LOG.debug(e);
return (total_filled+events)>0?(total_filled+events):-1;
}
}
// if we are here, then we have sufficient bytes to process the current state.
// Parse the buffer byte by byte (unless it is STATE_DATA)
byte b;
while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded))
{
switch (_state)
{
case START:
_state=_masked?State.MASK:State.OPCODE;
_bytesNeeded=_state.getNeeds();
continue;
case MASK:
_buffer.get(_mask,0,4);
available-=4;
_state=State.OPCODE;
_bytesNeeded=_state.getNeeds();
_m=0;
continue;
case OPCODE:
b=_buffer.get();
available--;
if (_masked)
b^=_mask[_m++%4];
_opcode=(byte)(b&0xf);
_flags=(byte)(0xf&(b>>4));
if (WebSocketConnectionD06.isControlFrame(_opcode)&&!WebSocketConnectionD06.isLastFrame(_flags))
{
_state=State.SKIP;
events++;
_handler.close(WebSocketConnectionD06.CLOSE_PROTOCOL,"fragmented control");
}
else
_state=State.LENGTH_7;
_bytesNeeded=_state.getNeeds();
continue;
case LENGTH_7:
b=_buffer.get();
available--;
if (_masked)
b^=_mask[_m++%4];
switch(b)
{
case 127:
_length=0;
_state=State.LENGTH_63;
_bytesNeeded=_state.getNeeds();
break;
case 126:
_length=0;
_state=State.LENGTH_16;
_bytesNeeded=_state.getNeeds();
break;
default:
_length=(0x7f&b);
_bytesNeeded=(int)_length;
_state=State.DATA;
}
continue;
case LENGTH_16:
b=_buffer.get();
available--;
if (_masked)
b^=_mask[_m++%4];
_length = _length*0x100 + (0xff&b);
if (--_bytesNeeded==0)
{
_bytesNeeded=(int)_length;
if (_length>_buffer.capacity())
{
_state=State.SKIP;
events++;
_handler.close(WebSocketConnectionD06.CLOSE_LARGE,"frame size "+_length+">"+_buffer.capacity());
}
else
{
_state=State.DATA;
}
}
continue;
case LENGTH_63:
b=_buffer.get();
available--;
if (_masked)
b^=_mask[_m++%4];
_length = _length*0x100 + (0xff&b);
if (--_bytesNeeded==0)
{
_bytesNeeded=(int)_length;
if (_length>=_buffer.capacity())
{
_state=State.SKIP;
events++;
_handler.close(WebSocketConnectionD06.CLOSE_LARGE,"frame size "+_length+">"+_buffer.capacity());
}
else
{
_state=State.DATA;
}
}
continue;
case SKIP:
int skip=Math.min(available,_bytesNeeded);
_buffer.skip(skip);
available-=skip;
_bytesNeeded-=skip;
if (_bytesNeeded==0)
_state=State.START;
}
}
if (_state==State.DATA && available>=_bytesNeeded)
{
ByteBuffer data =_buffer.get(_bytesNeeded);
if (_masked)
{
if (data.array()==null)
data=_buffer.asMutableBuffer();
byte[] array = data.array();
final int end=data.putIndex();
for (int i=data.getIndex();i<end;i++)
array[i]^=_mask[_m++%4];
}
// System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
events++;
_handler.onFrame(_flags, _opcode, data);
_bytesNeeded=0;
_state=State.START;
if (_buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
return total_filled+events;
}
}
}
/* ------------------------------------------------------------ */
public void fill(ByteBuffer buffer)
{
if (buffer!=null && buffer.length()>0)
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
_buffer.put(buffer);
buffer.clear();
}
}
}

View File

@ -1,388 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.
*******************************************************************************/
// ========================================================================
// 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 org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* Parser the WebSocket protocol.
*
*/
public class WebSocketParserD08 implements WebSocketParser
{
private static final Logger LOG = Log.getLogger(WebSocketParserD08.class);
public enum State {
START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1);
int _needs;
State(int needs)
{
_needs=needs;
}
int getNeeds()
{
return _needs;
}
}
private final WebSocketBuffers _buffers;
private final EndPoint _endp;
private final FrameHandler _handler;
private final boolean _shouldBeMasked;
private State _state;
private ByteBuffer _buffer;
private byte _flags;
private byte _opcode;
private int _bytesNeeded;
private long _length;
private boolean _masked;
private final byte[] _mask = new byte[4];
private int _m;
private boolean _skip;
private boolean _fakeFragments=true;
/* ------------------------------------------------------------ */
/**
* @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used.
* This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data
* is mostly used.
* @param endp the endpoint
* @param handler the handler to notify when a parse event occurs
* @param shouldBeMasked whether masking should be handled
*/
public WebSocketParserD08(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean shouldBeMasked)
{
_buffers=buffers;
_endp=endp;
_handler=handler;
_shouldBeMasked=shouldBeMasked;
_state=State.START;
}
/* ------------------------------------------------------------ */
/**
* @return True if fake fragments should be created for frames larger than the buffer.
*/
public boolean isFakeFragments()
{
return _fakeFragments;
}
/* ------------------------------------------------------------ */
/**
* @param fakeFragments True if fake fragments should be created for frames larger than the buffer.
*/
public void setFakeFragments(boolean fakeFragments)
{
_fakeFragments = fakeFragments;
}
/* ------------------------------------------------------------ */
public boolean isBufferEmpty()
{
return _buffer==null || _buffer.length()==0;
}
/* ------------------------------------------------------------ */
public ByteBuffer getBuffer()
{
return _buffer;
}
/* ------------------------------------------------------------ */
/** Parse to next event.
* Parse to the next {@link WebSocketParser.FrameHandler} event or until no more data is
* available. Fill data from the {@link EndPoint} only as necessary.
* @return An indication of progress or otherwise. -1 indicates EOF, 0 indicates
* that no bytes were read and no messages parsed. A positive number indicates either
* the bytes filled or the messages parsed.
*/
public int parseNext()
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
int total_filled=0;
int events=0;
// Loop until a datagram call back or can't fill anymore
while(true)
{
int available=_buffer.length();
// Fill buffer if we need a byte or need length
while (available<(_state==State.SKIP?1:_bytesNeeded))
{
// compact to mark (set at start of data)
_buffer.compact();
// if no space, then the data is too big for buffer
if (_buffer.space() == 0)
{
// Can we send a fake frame?
if (_fakeFragments && _state==State.DATA)
{
ByteBuffer data =_buffer.get(4*(available/4));
_buffer.compact();
if (_masked)
{
if (data.array()==null)
data=_buffer.asMutableBuffer();
byte[] array = data.array();
final int end=data.putIndex();
for (int i=data.getIndex();i<end;i++)
array[i]^=_mask[_m++%4];
}
// System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
events++;
_bytesNeeded-=data.length();
_handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD08.FLAG_FIN)), _opcode, data);
_opcode=WebSocketConnectionD08.OP_CONTINUATION;
}
if (_buffer.space() == 0)
throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity());
}
// catch IOExceptions (probably EOF) and try to parse what we have
try
{
int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
if (filled<=0)
return (total_filled+events)>0?(total_filled+events):filled;
total_filled+=filled;
available=_buffer.length();
}
catch(IOException e)
{
LOG.debug(e);
return (total_filled+events)>0?(total_filled+events):-1;
}
}
// if we are here, then we have sufficient bytes to process the current state.
// Parse the buffer byte by byte (unless it is STATE_DATA)
byte b;
while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded))
{
switch (_state)
{
case START:
_skip=false;
_state=State.OPCODE;
_bytesNeeded=_state.getNeeds();
continue;
case OPCODE:
b=_buffer.get();
available--;
_opcode=(byte)(b&0xf);
_flags=(byte)(0xf&(b>>4));
if (WebSocketConnectionD08.isControlFrame(_opcode)&&!WebSocketConnectionD08.isLastFrame(_flags))
{
events++;
LOG.warn("Fragmented Control from "+_endp);
_handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"Fragmented control");
_skip=true;
}
_state=State.LENGTH_7;
_bytesNeeded=_state.getNeeds();
continue;
case LENGTH_7:
b=_buffer.get();
available--;
_masked=(b&0x80)!=0;
b=(byte)(0x7f&b);
switch(b)
{
case 0x7f:
_length=0;
_state=State.LENGTH_63;
break;
case 0x7e:
_length=0;
_state=State.LENGTH_16;
break;
default:
_length=(0x7f&b);
_state=_masked?State.MASK:State.PAYLOAD;
}
_bytesNeeded=_state.getNeeds();
continue;
case LENGTH_16:
b=_buffer.get();
available--;
_length = _length*0x100 + (0xff&b);
if (--_bytesNeeded==0)
{
if (_length>_buffer.capacity() && !_fakeFragments)
{
events++;
_handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"frame size "+_length+">"+_buffer.capacity());
_skip=true;
}
_state=_masked?State.MASK:State.PAYLOAD;
_bytesNeeded=_state.getNeeds();
}
continue;
case LENGTH_63:
b=_buffer.get();
available--;
_length = _length*0x100 + (0xff&b);
if (--_bytesNeeded==0)
{
_bytesNeeded=(int)_length;
if (_length>=_buffer.capacity() && !_fakeFragments)
{
events++;
_handler.close(WebSocketConnectionD08.CLOSE_BADDATA,"frame size "+_length+">"+_buffer.capacity());
_skip=true;
}
_state=_masked?State.MASK:State.PAYLOAD;
_bytesNeeded=_state.getNeeds();
}
continue;
case MASK:
_buffer.get(_mask,0,4);
_m=0;
available-=4;
_state=State.PAYLOAD;
_bytesNeeded=_state.getNeeds();
break;
case PAYLOAD:
_bytesNeeded=(int)_length;
_state=_skip?State.SKIP:State.DATA;
break;
case DATA:
break;
case SKIP:
int skip=Math.min(available,_bytesNeeded);
_buffer.skip(skip);
available-=skip;
_bytesNeeded-=skip;
if (_bytesNeeded==0)
_state=State.START;
}
}
if (_state==State.DATA && available>=_bytesNeeded)
{
if ( _masked!=_shouldBeMasked)
{
_buffer.skip(_bytesNeeded);
_state=State.START;
events++;
_handler.close(WebSocketConnectionD08.CLOSE_PROTOCOL,"bad mask");
}
else
{
ByteBuffer data =_buffer.get(_bytesNeeded);
if (_masked)
{
if (data.array()==null)
data=_buffer.asMutableBuffer();
byte[] array = data.array();
final int end=data.putIndex();
for (int i=data.getIndex();i<end;i++)
array[i]^=_mask[_m++%4];
}
// System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
events++;
_handler.onFrame(_flags, _opcode, data);
_bytesNeeded=0;
_state=State.START;
}
return total_filled+events;
}
}
}
/* ------------------------------------------------------------ */
public void fill(ByteBuffer buffer)
{
if (buffer!=null && buffer.length()>0)
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
_buffer.put(buffer);
buffer.clear();
}
}
/* ------------------------------------------------------------ */
public void returnBuffer()
{
if (_buffer!=null && _buffer.length()==0)
{
_buffers.returnBuffer(_buffer);
_buffer=null;
}
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
ByteBuffer buffer=_buffer;
return WebSocketParserD08.class.getSimpleName()+"@"+ Integer.toHexString(hashCode())+"|"+_state+"|"+(buffer==null?"<>":buffer.toDetailString());
}
}

View File

@ -29,9 +29,9 @@
package org.eclipse.jetty.websocket;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;

View File

@ -1,102 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.QuotedStringTokenizer;
public class WebSocketServletConnectionD00 extends WebSocketConnectionD00 implements WebSocketServletConnection
{
private final WebSocketFactory factory;
public WebSocketServletConnectionD00(WebSocketFactory factory, WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol);
this.factory = factory;
}
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String uri = request.getRequestURI();
String query = request.getQueryString();
if (query != null && query.length() > 0)
{
uri += "?" + query;
}
uri = new HttpURI(uri).toString();
String host = request.getHeader("Host");
String origin = request.getHeader("Sec-WebSocket-Origin");
if (origin == null)
{
origin = request.getHeader("Origin");
}
if (origin != null)
{
origin = QuotedStringTokenizer.quoteIfNeeded(origin,"\r\n");
}
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");
if (origin != null)
{
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();
onFrameHandshake();
onWebsocketOpen();
}
}
@Override
public void onClose()
{
super.onClose();
factory.removeConnection(this);
}
}

View File

@ -1,60 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EndPoint;
public class WebSocketServletConnectionD06 extends WebSocketConnectionD06 implements WebSocketServletConnection
{
private final WebSocketFactory factory;
public WebSocketServletConnectionD06(WebSocketFactory factory, WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol);
this.factory = factory;
}
/* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
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);
onFrameHandshake();
onWebSocketOpen();
}
@Override
public void onClose()
{
super.onClose();
factory.removeConnection(this);
}
}

View File

@ -1,66 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EndPoint;
public class WebSocketServletConnectionD08 extends WebSocketConnectionD08 implements WebSocketServletConnection
{
private final WebSocketFactory factory;
public WebSocketServletConnectionD08(WebSocketFactory factory, WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
List<Extension> extensions, int draft) throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft);
this.factory = factory;
}
/* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
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);
}
for (Extension ext : getExtensions())
{
response.addHeader("Sec-WebSocket-Extensions",ext.getParameterizedName());
}
response.sendError(101);
onFrameHandshake();
onWebSocketOpen();
}
@Override
public void onClose()
{
super.onClose();
factory.removeConnection(this);
}
}

View File

@ -1,125 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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.net.URI;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.helper.CaptureSocket;
import org.eclipse.jetty.websocket.helper.SafariD00;
import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
public class SafariWebsocketDraft0Test
{
private Server server;
private WebSocketCaptureServlet servlet;
private URI serverUri;
@BeforeClass
public static void initLogging()
{
// Configure Logging
// System.setProperty("org.eclipse.jetty.util.log.class",StdErrLog.class.getName());
// System.setProperty("org.eclipse.jetty.LEVEL","DEBUG");
}
@Before
public void startServer() throws Exception
{
// Configure Server
server = new Server(0);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
// Serve capture servlet
servlet = new WebSocketCaptureServlet();
context.addServlet(new ServletHolder(servlet),"/");
// Start Server
server.start();
Connector conn = server.getConnectors()[0];
String host = conn.getHost();
if (host == null)
{
host = "localhost";
}
int port = conn.getLocalPort();
serverUri = new URI(String.format("ws://%s:%d/",host,port));
// System.out.printf("Server URI: %s%n",serverUri);
}
@Test
public void testSendTextMessages() throws Exception
{
SafariD00 safari = new SafariD00(serverUri);
try
{
safari.connect();
safari.issueHandshake();
// Send 5 short messages, using technique seen in Safari.
safari.sendMessage("aa-0"); // single msg
safari.sendMessage("aa-1", "aa-2", "aa-3", "aa-4");
// Servlet should show only 1 connection.
Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1));
CaptureSocket socket = servlet.captures.get(0);
Assert.assertThat("CaptureSocket",socket,notNullValue());
Assert.assertThat("CaptureSocket.isConnected", socket.awaitConnected(10000), is(true));
// Give servlet time to process messages
for (int i=0;i<100 && socket.messages.size()<5;i++)
threadSleep(100,TimeUnit.MILLISECONDS);
// Should have captured 5 messages.
Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5));
}
finally
{
// System.out.println("Closing client socket");
safari.disconnect();
}
}
public static void threadSleep(int dur, TimeUnit unit) throws InterruptedException
{
long ms = TimeUnit.MILLISECONDS.convert(dur,unit);
Thread.sleep(ms);
}
@After
public void stopServer() throws Exception
{
server.stop();
}
}

View File

@ -1,91 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 static junit.framework.Assert.*;
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;
public class WebSocketGeneratorD00Test
{
private ByteArrayBuffer _out;
private WebSocketGenerator _generator;
@Before
public void setUp() throws Exception
{
WebSocketBuffers buffers = new WebSocketBuffers(1024);
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
_generator = new WebSocketGeneratorD00(buffers, endPoint);
_out = new ByteArrayBuffer(4096);
endPoint.setOut(_out);
}
@Test
public void testOneString() throws Exception
{
byte[] data="Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x0,(byte)0x04,data,0,data.length);
_generator.flush();
assertEquals((byte)0x04,_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());
assertEquals((byte)0xff,_out.get());
}
@Test
public void testOneBinaryString() throws Exception
{
byte[] data="Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x0,(byte)0x84,data,0,data.length);
_generator.flush();
assertEquals((byte)0x84,_out.get());
assertEquals(15,_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());
}
}

View File

@ -1,213 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 static junit.framework.Assert.*;
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;
public class WebSocketGeneratorD06Test
{
private ByteArrayBuffer _out;
private WebSocketGenerator _generator;
ByteArrayEndPoint _endPoint;
WebSocketBuffers _buffers;
byte[] _mask = new byte[4];
int _m;
public MaskGen _maskGen = new FixedMaskGen(
new byte[]{(byte)0x00,(byte)0x00,(byte)0x0f,(byte)0xff});
@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,null);
byte[] data = "Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,data,0,data.length);
_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,null);
String string = "Hell\uFF4F W\uFF4Frld";
byte[] bytes=string.getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,bytes,0,bytes.length);
_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,null);
byte[] b=new byte[150];
for (int i=0;i<b.length;i++)
b[i]=(byte)('0'+(i%10));
_generator.addFrame((byte)0x8,(byte)0x4,b,0,b.length);
_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,_maskGen);
byte[] data = "Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,data,0,data.length);
_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,_maskGen);
String string = "Hell\uFF4F W\uFF4Frld";
byte[] bytes=string.getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,bytes,0,bytes.length);
_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,_maskGen);
byte[] b=new byte[150];
for (int i=0;i<b.length;i++)
b[i]=(byte)('0'+(i%10));
_generator.addFrame((byte)0x8,(byte)0x04,b,0,b.length);
_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());
}
}

View File

@ -1,212 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 static junit.framework.Assert.*;
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;
public class WebSocketGeneratorD08Test
{
private ByteArrayBuffer _out;
private WebSocketGenerator _generator;
ByteArrayEndPoint _endPoint;
WebSocketBuffers _buffers;
byte[] _mask = new byte[4];
int _m;
public MaskGen _maskGen = new FixedMaskGen(
new byte[]{(byte)0x00,(byte)0x00,(byte)0x0f,(byte)0xff});
@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 WebSocketGeneratorD08(_buffers, _endPoint,null);
byte[] data = "Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,data,0,data.length);
_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 WebSocketGeneratorD08(_buffers, _endPoint,null);
String string = "Hell\uFF4F W\uFF4Frld";
byte[] bytes=string.getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,bytes,0,bytes.length);
_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 WebSocketGeneratorD08(_buffers, _endPoint,null);
byte[] b=new byte[150];
for (int i=0;i<b.length;i++)
b[i]=(byte)('0'+(i%10));
_generator.addFrame((byte)0x8,(byte)0x4,b,0,b.length);
_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 WebSocketGeneratorD08(_buffers, _endPoint,_maskGen);
byte[] data = "Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,data,0,data.length);
_generator.flush();
assertEquals((byte)0x84,_out.get());
assertEquals(15,0x7f&_out.get());
_out.get(_mask,0,4);
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 WebSocketGeneratorD08(_buffers, _endPoint,_maskGen);
String string = "Hell\uFF4F W\uFF4Frld";
byte[] bytes=string.getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,bytes,0,bytes.length);
_generator.flush();
assertEquals((byte)0x84,_out.get());
assertEquals(15,0x7f&_out.get());
_out.get(_mask,0,4);
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 WebSocketGeneratorD08(_buffers, _endPoint,_maskGen);
byte[] b=new byte[150];
for (int i=0;i<b.length;i++)
b[i]=(byte)('0'+(i%10));
_generator.addFrame((byte)0x8,(byte)0x04,b,0,b.length);
_generator.flush();
assertEquals((byte)0x84,_out.get());
assertEquals((byte)126,0x7f&_out.get());
assertEquals((byte)0,_out.get());
assertEquals((byte)b.length,_out.get());
_out.get(_mask,0,4);
for (int i=0;i<b.length;i++)
assertEquals('0'+(i%10),0xff&getMasked());
}
}

View File

@ -1,810 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 static org.junit.Assert.*;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.http.HttpServletRequest;
import junit.framework.Assert;
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.TypeUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class WebSocketMessageD00Test
{
private static Server __server;
private static Connector __connector;
private static TestWebSocket __serverWebSocket;
private static CountDownLatch __latch;
private static AtomicInteger __textCount = new AtomicInteger(0);
@BeforeClass
public static void startServer() throws Exception
{
__server = new Server();
__connector = new SelectChannelConnector();
__server.addConnector(__connector);
WebSocketHandler wsHandler = new WebSocketHandler()
{
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
{
__serverWebSocket = new TestWebSocket();
__serverWebSocket._onConnect=("onConnect".equals(protocol));
__serverWebSocket._echo=("echo".equals(protocol));
__serverWebSocket._latch=("latch".equals(protocol));
if (__serverWebSocket._latch)
__latch=new CountDownLatch(1);
return __serverWebSocket;
}
};
wsHandler.setHandler(new DefaultHandler());
__server.setHandler(wsHandler);
__server.start();
}
@AfterClass
public static void stopServer() throws Exception
{
__server.stop();
__server.join();
}
@Before
public void reset()
{
__textCount.set(0);
}
@Test
public void testServerSendBigStringMessage() throws Exception
{
Socket socket = new Socket("localhost", __connector.getLocalPort());
OutputStream output = socket.getOutputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").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();
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "ISO-8859-1"));
String responseLine = reader.readLine();
assertTrue(responseLine.startsWith("HTTP/1.1 101 WebSocket Protocol Handshake"));
// Read until we find an empty line, which signals the end of the http response
String line;
while ((line = reader.readLine()) != null)
if (line.length() == 0)
break;
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.outbound);
// read the hixie bytes
char[] hixie=new char[16]; // should be bytes, but we know this example is all ascii
int h=16;
int o=0;
do
{
int l=reader.read(hixie,o,h);
if (l<0)
break;
h-=l;
o+=l;
}
while (h>0);
assertEquals("8jKS'y:G*Co,Wxa-",new String(hixie,0,16));
// Server sends a big message
StringBuilder message = new StringBuilder();
String text = "0123456789ABCDEF";
for (int i = 0; i < 64 * 1024 / text.length(); ++i)
message.append(text);
__serverWebSocket.outbound.sendMessage(message.toString());
// Read until we get 0xFF
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (true)
{
int read = input.read();
baos.write(read);
if (read == 0xFF)
break;
}
baos.close();
byte[] bytes = baos.toByteArray();
String result = StringUtil.printable(bytes);
assertTrue(result.startsWith("0x00"));
assertTrue(result.endsWith("0xFF"));
assertEquals(message.length() + "0x00".length() + "0xFF".length(), result.length());
}
@Test
public void testServerSendOnConnect() throws Exception
{
Socket socket = new Socket("localhost", __connector.getLocalPort());
OutputStream output = socket.getOutputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Protocol: onConnect\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").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();
String looking_for="HTTP/1.1 101 WebSocket Protocol Handshake\r\n";
while(true)
{
int b = input.read();
if (b<0)
throw new EOFException();
assertEquals((int)looking_for.charAt(0),b);
if (looking_for.length()==1)
break;
looking_for=looking_for.substring(1);
}
String skipping_for="\r\n\r\n";
int state=0;
while(true)
{
int b = input.read();
if (b<0)
throw new EOFException();
if (b==skipping_for.charAt(state))
{
state++;
if (state==skipping_for.length())
break;
}
else
state=0;
}
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.outbound);
looking_for="8jKS'y:G*Co,Wxa-";
while(true)
{
int b = input.read();
if (b<0)
throw new EOFException();
assertEquals((int)looking_for.charAt(0),b);
if (looking_for.length()==1)
break;
looking_for=looking_for.substring(1);
}
assertEquals(0x00,input.read());
looking_for="sent on connect";
while(true)
{
int b = input.read();
if (b<0)
throw new EOFException();
assertEquals((int)looking_for.charAt(0),b);
if (looking_for.length()==1)
break;
looking_for=looking_for.substring(1);
}
assertEquals(0xff,input.read());
}
@Test
public void testServerEcho() throws Exception
{
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
Socket socket = new Socket("localhost", __connector.getLocalPort());
socket.setSoTimeout(1000);
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Protocol: echo\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").getBytes("ISO-8859-1"));
output.flush();
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
skipTo("\r\n\r\n",input);
lookFor("8jKS'y:G*Co,Wxa-",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
output.write(0x00);
byte[] bytes="this is an echo".getBytes(StringUtil.__ISO_8859_1);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]);
output.write(0xff);
output.flush();
assertEquals("00",TypeUtil.toHexString((byte)(0xff&input.read())));
lookFor("this is an echo",input);
assertEquals(0xff,input.read());
}
@Test
public void testBlockedConsumer() throws Exception
{
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
Socket socket = new Socket("localhost", __connector.getLocalPort());
socket.setSoTimeout(60000);
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Protocol: latch\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").getBytes("ISO-8859-1"));
output.flush();
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
skipTo("\r\n\r\n",input);
lookFor("8jKS'y:G*Co,Wxa-",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
__serverWebSocket.connection.setMaxIdleTime(60000);
byte[] bytes="This is a long message of text that we will send again and again".getBytes(StringUtil.__ISO_8859_1);
byte[] mesg=new byte[bytes.length+2];
mesg[0]=(byte)0x00;
for (int i=0;i<bytes.length;i++)
mesg[i+1]=(byte)(bytes[i]);
mesg[mesg.length-1]=(byte)0xFF;
final int count = 100000;
// Send and receive 1 message
output.write(mesg);
output.flush();
while(__textCount.get()==0)
Thread.sleep(10);
// unblock the latch in 4s
new Thread()
{
@Override
public void run()
{
try
{
Thread.sleep(4000);
__latch.countDown();
//System.err.println("latched");
}
catch(Exception e)
{
e.printStackTrace();
}
}
}.start();
// Send enough messages to fill receive buffer
long max=0;
long start=System.currentTimeMillis();
for (int i=0;i<count;i++)
{
output.write(mesg);
if (i%100==0)
{
// System.err.println(">>> "+i);
output.flush();
long now=System.currentTimeMillis();
long duration=now-start;
start=now;
if (max<duration)
max=duration;
}
}
Thread.sleep(50);
while(__textCount.get()<count+1)
{
System.err.println(__textCount.get()+"<"+(count+1));
Thread.sleep(10);
}
assertEquals(count+1,__textCount.get()); // all messages
assertTrue(max>2000); // was blocked
}
@Test
public void testBlockedProducer() throws Exception
{
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
final Socket socket = new Socket("localhost", __connector.getLocalPort());
socket.setSoTimeout(60000);
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Protocol: latch\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").getBytes("ISO-8859-1"));
output.flush();
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
skipTo("\r\n\r\n",input);
lookFor("8jKS'y:G*Co,Wxa-",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
final int count = 100000;
__serverWebSocket.connection.setMaxIdleTime(60000);
__latch.countDown();
// wait 2s and then consume messages
final AtomicLong totalB=new AtomicLong();
new Thread()
{
@Override
public void run()
{
try
{
Thread.sleep(2000);
byte[] recv = new byte[32*1024];
int len=0;
while (len>=0)
{
totalB.addAndGet(len);
len=socket.getInputStream().read(recv,0,recv.length);
Thread.sleep(10);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}.start();
// Send enough messages to fill receive buffer
long max=0;
long start=System.currentTimeMillis();
String mesg="How Now Brown Cow";
for (int i=0;i<count;i++)
{
__serverWebSocket.connection.sendMessage(mesg);
if (i%100==0)
{
output.flush();
long now=System.currentTimeMillis();
long duration=now-start;
start=now;
if (max<duration)
max=duration;
}
}
while(totalB.get()<(count*(mesg.length()+2)))
Thread.sleep(100);
assertEquals(count*(mesg.length()+2),totalB.get()); // all messages
assertTrue(max>1000); // was blocked
}
@Test
public void testIdle() throws Exception
{
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
final Socket socket = new Socket("localhost", __connector.getLocalPort());
socket.setSoTimeout(10000);
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Protocol: onConnect\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").getBytes("ISO-8859-1"));
output.flush();
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
skipTo("\r\n\r\n",input);
lookFor("8jKS'y:G*Co,Wxa-",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
__serverWebSocket.connection.setMaxIdleTime(250);
assertEquals(0x00,input.read());
lookFor("sent on connect",input);
assertEquals(0xff,input.read());
assertEquals(-1,input.read());
socket.close();
assertTrue(__serverWebSocket.awaitDisconnected(100));
}
@Test
public void testIdleBadClient() throws Exception
{
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
final Socket socket = new Socket("localhost", __connector.getLocalPort());
socket.setSoTimeout(10000);
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Protocol: onConnect\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").getBytes("ISO-8859-1"));
output.flush();
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
skipTo("\r\n\r\n",input);
lookFor("8jKS'y:G*Co,Wxa-",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
__serverWebSocket.connection.setMaxIdleTime(250);
assertEquals(0x00,input.read());
lookFor("sent on connect",input);
assertEquals(0xff,input.read());
assertEquals(-1,input.read());
assertTrue(__serverWebSocket.disconnected.getCount()>0);
assertTrue(__serverWebSocket.awaitDisconnected(1000));
}
@Test
public void testTCPClose() throws Exception
{
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
final Socket socket = new Socket("localhost", __connector.getLocalPort());
socket.setSoTimeout(10000);
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Protocol: onConnect\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").getBytes("ISO-8859-1"));
output.flush();
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
skipTo("\r\n\r\n",input);
lookFor("8jKS'y:G*Co,Wxa-",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
__serverWebSocket.connection.setMaxIdleTime(250);
assertEquals(0x00,input.read());
lookFor("sent on connect",input);
assertEquals(0xff,input.read());
socket.close();
assertTrue(__serverWebSocket.awaitDisconnected(500));
try
{
__serverWebSocket.connection.sendMessage("Don't send");
assertTrue(false);
}
catch(IOException e)
{
assertTrue(true);
}
}
@Test
public void testTCPHalfClose() throws Exception
{
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
final Socket socket = new Socket("localhost", __connector.getLocalPort());
socket.setSoTimeout(10000);
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(
("GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Upgrade: WebSocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Protocol: onConnect\r\n" +
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
"\r\n"+
"^n:ds[4U").getBytes("ISO-8859-1"));
output.flush();
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
skipTo("\r\n\r\n",input);
lookFor("8jKS'y:G*Co,Wxa-",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
__serverWebSocket.connection.setMaxIdleTime(250);
assertEquals(0x00,input.read());
lookFor("sent on connect",input);
assertEquals(0xff,input.read());
socket.shutdownOutput();
assertTrue(__serverWebSocket.awaitDisconnected(500));
assertEquals(-1,input.read());
// look for broken pipe
try
{
for (int i=0;i<1000;i++)
output.write(0);
Assert.fail();
}
catch(SocketException e)
{
// expected
}
}
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.OnFrame, WebSocket, WebSocket.OnTextMessage
{
protected boolean _latch;
boolean _echo=true;
boolean _onConnect=false;
private volatile Connection outbound;
private final CountDownLatch connected = new CountDownLatch(1);
private final CountDownLatch disconnected = new CountDownLatch(1);
private volatile FrameConnection connection;
public FrameConnection getConnection()
{
return connection;
}
public void onHandshake(FrameConnection connection)
{
this.connection = connection;
}
public void onOpen(Connection 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 onClose(int code,String message)
{
disconnected.countDown();
}
public boolean onFrame(byte flags, byte opcode, byte[] data, int offset, int length)
{
return true;
}
public void onMessage(String data)
{
__textCount.incrementAndGet();
if (_latch)
{
try
{
__latch.await();
}
catch(Exception e)
{
e.printStackTrace();
}
}
if (_echo)
{
try
{
outbound.sendMessage(data);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}

View File

@ -1,856 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 static org.junit.Assert.*;
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 java.util.concurrent.atomic.AtomicReference;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.io.ByteArrayEndPoint;
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.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
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()
{
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
{
_serverWebSocket = new TestWebSocket();
_serverWebSocket.onConnect=("onConnect".equals(protocol));
_serverWebSocket.echo=("echo".equals(protocol));
_serverWebSocket.aggregate=("aggregate".equals(protocol));
return _serverWebSocket;
}
};
wsHandler.getWebSocketFactory().setBufferSize(8192);
wsHandler.getWebSocketFactory().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.connection);
// 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.connection.sendMessage(data);
assertEquals(WebSocketConnectionD06.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.connection);
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);
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.connection);
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.connection);
socket.setSoTimeout(1000);
assertEquals(0x83,input.read());
assertEquals(0x00,input.read());
}
@Test
public void testMaxTextSize() 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: other\r\n" +
"Sec-WebSocket-Version: 6\r\n"+
"\r\n").getBytes("ISO-8859-1"));
output.flush();
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.connection);
_serverWebSocket.getConnection().setMaxTextMessageSize(15);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0x04^0xff);
output.write(0x0a^0xff);
byte[] bytes="0123456789".getBytes(StringUtil.__ISO_8859_1);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]^0xff);
output.flush();
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0x80^0xff);
output.write(0x0a^0xff);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]^0xff);
output.flush();
assertEquals(0x80|WebSocketConnectionD06.OP_CLOSE,input.read());
assertEquals(30,input.read());
int code=(0xff&input.read())*0x100+(0xff&input.read());
assertEquals(1004,code);
lookFor("Text message size > 15 chars",input);
}
@Test
public void testMaxTextSize2() 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: other\r\n" +
"Sec-WebSocket-Version: 6\r\n"+
"\r\n").getBytes("ISO-8859-1"));
output.flush();
socket.setSoTimeout(100000);
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.connection);
_serverWebSocket.getConnection().setMaxTextMessageSize(15);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0x04^0xff);
output.write(0x14^0xff);
byte[] bytes="01234567890123456789".getBytes(StringUtil.__ISO_8859_1);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]^0xff);
output.flush();
assertEquals(0x80|WebSocketConnectionD06.OP_CLOSE,input.read());
assertEquals(30,input.read());
int code=(0xff&input.read())*0x100+(0xff&input.read());
assertEquals(1004,code);
lookFor("Text message size > 15 chars",input);
}
@Test
public void testBinaryAggregate() 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: aggregate\r\n" +
"Sec-WebSocket-Version: 6\r\n"+
"\r\n").getBytes("ISO-8859-1"));
output.flush();
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.connection);
_serverWebSocket.getConnection().setMaxBinaryMessageSize(1024);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(WebSocketConnectionD06.OP_BINARY^0xff);
output.write(0x0a^0xff);
byte[] bytes="0123456789".getBytes(StringUtil.__ISO_8859_1);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]^0xff);
output.flush();
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0x80^0xff);
output.write(0x0a^0xff);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]^0xff);
output.flush();
assertEquals(0x85,input.read());
assertEquals(20,input.read());
lookFor("01234567890123456789",input);
}
@Test
public void testMaxBinarySize() 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: other\r\n" +
"Sec-WebSocket-Version: 6\r\n"+
"\r\n").getBytes("ISO-8859-1"));
output.flush();
socket.setSoTimeout(100000);
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.connection);
_serverWebSocket.getConnection().setMaxBinaryMessageSize(15);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0x0f^0xff);
output.write(0x0a^0xff);
byte[] bytes="0123456789".getBytes(StringUtil.__ISO_8859_1);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]^0xff);
output.flush();
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0x80^0xff);
output.write(0x0a^0xff);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]^0xff);
output.flush();
assertEquals(0x80|WebSocketConnectionD06.OP_CLOSE,input.read());
assertEquals(19,input.read());
int code=(0xff&input.read())*0x100+(0xff&input.read());
assertEquals(1004,code);
lookFor("Message size > 15",input);
}
@Test
public void testMaxBinarySize2() 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: other\r\n" +
"Sec-WebSocket-Version: 6\r\n"+
"\r\n").getBytes("ISO-8859-1"));
output.flush();
socket.setSoTimeout(100000);
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.connection);
_serverWebSocket.getConnection().setMaxBinaryMessageSize(15);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0xff);
output.write(0x0f^0xff);
output.write(0x14^0xff);
byte[] bytes="01234567890123456789".getBytes(StringUtil.__ISO_8859_1);
for (int i=0;i<bytes.length;i++)
output.write(bytes[i]^0xff);
output.flush();
assertEquals(0x80|WebSocketConnectionD06.OP_CLOSE,input.read());
assertEquals(19,input.read());
int code=(0xff&input.read())*0x100+(0xff&input.read());
assertEquals(1004,code);
lookFor("Message size > 15",input);
}
@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.connection);
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/0x100,input.read());
assertEquals(1000%0x100,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.connection.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.connection);
assertEquals(0x84,input.read());
assertEquals(0x0f,input.read());
lookFor("sent on connect",input);
socket.close();
assertTrue(_serverWebSocket.awaitDisconnected(500));
try
{
_serverWebSocket.connection.sendMessage("Don't send");
assertTrue(false);
}
catch(IOException e)
{
assertTrue(true);
}
}
@Test
public void testParserAndGenerator() throws Exception
{
String message = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
final AtomicReference<String> received = new AtomicReference<String>();
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096);
WebSocketGeneratorD06 gen = new WebSocketGeneratorD06(new WebSocketBuffers(8096),endp,null);
byte[] data = message.getBytes(StringUtil.__UTF8);
gen.addFrame((byte)0x8,(byte)0x4,data,0,data.length);
endp = new ByteArrayEndPoint(endp.getOut().asArray(),4096);
WebSocketParserD06 parser = new WebSocketParserD06(new WebSocketBuffers(8096),endp,new WebSocketParser.FrameHandler()
{
public void onFrame(byte flags, byte opcode, ByteBuffer buffer)
{
received.set(buffer.toString());
}
public void close(int code,String message)
{
}
},false);
parser.parseNext();
assertEquals(message,received.get());
}
@Test
public void testParserAndGeneratorMasked() throws Exception
{
String message = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
final AtomicReference<String> received = new AtomicReference<String>();
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096);
MaskGen maskGen = new RandomMaskGen();
WebSocketGeneratorD06 gen = new WebSocketGeneratorD06(new WebSocketBuffers(8096),endp,maskGen);
byte[] data = message.getBytes(StringUtil.__UTF8);
gen.addFrame((byte)0x8,(byte)0x4,data,0,data.length);
endp = new ByteArrayEndPoint(endp.getOut().asArray(),4096);
WebSocketParserD06 parser = new WebSocketParserD06(new WebSocketBuffers(8096),endp,new WebSocketParser.FrameHandler()
{
public void onFrame(byte flags, byte opcode, ByteBuffer buffer)
{
received.set(buffer.toString());
}
public void close(int code,String message)
{
}
},true);
parser.parseNext();
assertEquals(message,received.get());
}
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.OnFrame, WebSocket.OnBinaryMessage, WebSocket.OnTextMessage
{
boolean onConnect=false;
boolean echo=true;
boolean aggregate=false;
private final CountDownLatch connected = new CountDownLatch(1);
private final CountDownLatch disconnected = new CountDownLatch(1);
private volatile FrameConnection connection;
public Connection getConnection()
{
return connection;
}
public void onHandshake(FrameConnection connection)
{
this.connection = connection;
}
public void onOpen(Connection connection)
{
if (onConnect)
{
try
{
connection.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 onClose(int code,String message)
{
disconnected.countDown();
}
public boolean onFrame(byte flags, byte opcode, byte[] data, int offset, int length)
{
if (echo)
{
switch(opcode)
{
case WebSocketConnectionD06.OP_CLOSE:
case WebSocketConnectionD06.OP_PING:
case WebSocketConnectionD06.OP_PONG:
break;
default:
try
{
connection.sendFrame(flags,opcode,data,offset,length);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return false;
}
public void onMessage(byte[] data, int offset, int length)
{
if (aggregate)
{
try
{
connection.sendMessage(data,offset,length);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
public void onMessage(String data)
{
if (aggregate)
{
try
{
connection.sendMessage(data);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}

View File

@ -1,156 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.io.BufferCache.ByteBuffer;
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;
public class WebSocketParserD00Test
{
private ByteArrayBuffer _in;
private Handler _handler;
private WebSocketParser _parser;
@Before
public void setUp() throws Exception
{
WebSocketBuffers buffers = new WebSocketBuffers(1024);
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
_handler = new Handler();
_parser=new WebSocketParserD00(buffers, endPoint,_handler);
_in = new ByteArrayBuffer(2048);
endPoint.setIn(_in);
}
@Test
public void testCache() throws Exception
{
assertEquals(HttpHeaderValue.UPGRADE_ORDINAL ,((CachedBuffer)HttpHeaderValue.CACHE.lookup("Upgrade")).getOrdinal());
}
@Test
public void testOneUtf8() throws Exception
{
_in.put((byte)0x00);
_in.put("Hello World".getBytes(StringUtil.__UTF8));
_in.put((byte)0xff);
int filled =_parser.parseNext();
assertThat(filled,greaterThan(0));
assertEquals("Hello World",_handler._data.get(0));
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testTwoUtf8() throws Exception
{
_in.put((byte)0x00);
_in.put("Hello World".getBytes(StringUtil.__UTF8));
_in.put((byte)0xff);
_in.put((byte)0x00);
_in.put("Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8));
_in.put((byte)0xff);
int filled =_parser.parseNext();
assertThat(filled,greaterThan(0));
assertEquals("Hello World",_handler._data.get(0));
assertFalse(_parser.isBufferEmpty());
assertFalse(_parser.getBuffer()==null);
filled =_parser.parseNext();
assertThat(filled,greaterThan(0));
assertEquals("Hell\uFF4f W\uFF4Frld",_handler._data.get(1));
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testOneBinary() throws Exception
{
_in.put((byte)0x80);
_in.put((byte)11);
_in.put("Hello World".getBytes(StringUtil.__UTF8));
int filled =_parser.parseNext();
assertThat(filled,greaterThan(0));
assertEquals("Hello World",_handler._data.get(0));
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testTwoBinary() throws Exception
{
_in.put((byte)0x80);
_in.put((byte)11);
_in.put("Hello World".getBytes(StringUtil.__UTF8));
byte[] data = new byte[150];
for (int i=0;i<data.length;i++)
data[i]=(byte)('0'+(i%10));
_in.put((byte)0x80);
_in.put((byte)(0x80|(data.length>>7)));
_in.put((byte)(data.length&0x7f));
_in.put(data);
int filled =_parser.parseNext();
assertThat(filled,greaterThan(0));
assertEquals("Hello World",_handler._data.get(0));
assertFalse(_parser.isBufferEmpty());
assertFalse(_parser.getBuffer()==null);
filled =_parser.parseNext();
assertThat(filled,greaterThan(0));
String got=_handler._data.get(1);
assertEquals(data.length,got.length());
assertTrue(got.startsWith("012345678901234567890123"));
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
private class Handler implements WebSocketParser.FrameHandler
{
public List<String> _data = new ArrayList<String>();
public void onFrame(byte flags, byte opcode, ByteBuffer buffer)
{
_data.add(buffer.toString(StringUtil.__UTF8));
}
public void close(int code,String message)
{
}
}
}

View File

@ -1,327 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.io.BufferCache.ByteBuffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.junit.Before;
import org.junit.Test;
public class WebSocketParserD06Test
{
private MaskedByteArrayBuffer _in;
private Handler _handler;
private WebSocketParser _parser;
private byte[] _mask = new byte[] {(byte)0x00,(byte)0xF0,(byte)0x0F,(byte)0xFF};
private int _m;
class MaskedByteArrayBuffer extends ByteArrayBuffer
{
MaskedByteArrayBuffer()
{
super(4096);
}
public void sendMask()
{
super.poke(putIndex(),_mask,0,4);
super.setPutIndex(putIndex()+4);
_m=0;
}
@Override
public int put(ByteBuffer src)
{
return put(src.asArray(),0,src.length());
}
@Override
public void put(byte b)
{
super.put((byte)(b^_mask[_m++%4]));
}
@Override
public int put(byte[] b, int offset, int length)
{
byte[] mb = new byte[b.length];
final int end=offset+length;
for (int i=offset;i<end;i++)
{
mb[i]=(byte)(b[i]^_mask[_m++%4]);
}
return super.put(mb,offset,length);
}
@Override
public int put(byte[] b)
{
return put(b,0,b.length);
}
};
@Before
public void setUp() throws Exception
{
WebSocketBuffers buffers = new WebSocketBuffers(1024);
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
endPoint.setNonBlocking(true);
_handler = new Handler();
_parser=new WebSocketParserD06(buffers, endPoint,_handler,true);
_in = new MaskedByteArrayBuffer();
endPoint.setIn(_in);
}
@Test
public void testCache() throws Exception
{
assertEquals(HttpHeaderValue.UPGRADE_ORDINAL ,((CachedBuffer)HttpHeaderValue.CACHE.lookup("Upgrade")).getOrdinal());
}
@Test
public void testFlagsOppcode() throws Exception
{
_in.sendMask();
_in.put((byte)0xff);
_in.put((byte)0);
int filled =_parser.parseNext();
assertEquals(7,filled);
assertEquals(0xf,_handler._flags);
assertEquals(0xf,_handler._opcode);
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testShortText() throws Exception
{
_in.sendMask();
_in.put((byte)0x84);
_in.put((byte)11);
_in.put("Hello World".getBytes(StringUtil.__UTF8));
System.err.println("tosend="+TypeUtil.toHexString(_in.asArray()));
int filled =_parser.parseNext();
assertEquals(18,filled);
assertEquals("Hello World",_handler._data.get(0));
assertEquals(0x8,_handler._flags);
assertEquals(0x4,_handler._opcode);
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testShortUtf8() throws Exception
{
String string = "Hell\uFF4f W\uFF4Frld";
byte[] bytes = string.getBytes("UTF-8");
_in.sendMask();
_in.put((byte)0x84);
_in.put((byte)bytes.length);
_in.put(bytes);
int filled =_parser.parseNext();
assertEquals(bytes.length+7,filled);
assertEquals(string,_handler._data.get(0));
assertEquals(0x8,_handler._flags);
assertEquals(0x4,_handler._opcode);
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testMediumText() throws Exception
{
String string = "Hell\uFF4f Medium W\uFF4Frld ";
for (int i=0;i<4;i++)
string = string+string;
string += ". The end.";
byte[] bytes = string.getBytes(StringUtil.__UTF8);
_in.sendMask();
_in.put((byte)0x84);
_in.put((byte)0x7E);
_in.put((byte)(bytes.length>>8));
_in.put((byte)(bytes.length&0xff));
_in.put(bytes);
int filled =_parser.parseNext();
assertEquals(bytes.length+9,filled);
assertEquals(string,_handler._data.get(0));
assertEquals(0x8,_handler._flags);
assertEquals(0x4,_handler._opcode);
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testLongText() throws Exception
{
WebSocketBuffers buffers = new WebSocketBuffers(0x20000);
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
WebSocketParser parser=new WebSocketParserD06(buffers, endPoint,_handler,false);
ByteArrayBuffer in = new ByteArrayBuffer(0x20000);
endPoint.setIn(in);
String string = "Hell\uFF4f Big W\uFF4Frld ";
for (int i=0;i<12;i++)
string = string+string;
string += ". The end.";
byte[] bytes = string.getBytes("UTF-8");
_in.sendMask();
in.put((byte)0x84);
in.put((byte)0x7F);
in.put((byte)0x00);
in.put((byte)0x00);
in.put((byte)0x00);
in.put((byte)0x00);
in.put((byte)0x00);
in.put((byte)(bytes.length>>16));
in.put((byte)((bytes.length>>8)&0xff));
in.put((byte)(bytes.length&0xff));
in.put(bytes);
int filled =parser.parseNext();
assertEquals(bytes.length+11,filled);
assertEquals(string,_handler._data.get(0));
assertTrue(parser.isBufferEmpty());
assertTrue(parser.getBuffer()==null);
}
@Test
public void testShortFragmentTest() throws Exception
{
_in.sendMask();
_in.put((byte)0x04);
_in.put((byte)0x06);
_in.put("Hello ".getBytes(StringUtil.__UTF8));
_in.sendMask();
_in.put((byte)0x80);
_in.put((byte)0x05);
_in.put("World".getBytes(StringUtil.__UTF8));
int filled =_parser.parseNext();
assertEquals(24,filled);
assertEquals(0,_handler._data.size());
assertFalse(_parser.isBufferEmpty());
assertFalse(_parser.getBuffer()==null);
filled =_parser.parseNext();
assertEquals(1,filled);
assertEquals("Hello World",_handler._data.get(0));
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testFrameTooLarge() throws Exception
{
// Buffers are only 1024, so this frame is too large
_in.sendMask();
_in.put((byte)0x84);
_in.put((byte)0x7E);
_in.put((byte)(2048>>8));
_in.put((byte)(2048&0xff));
int filled =_parser.parseNext();
assertEquals(9,filled);
assertEquals(WebSocketConnectionD06.CLOSE_LARGE,_handler._code);
for (int i=0;i<2048;i++)
_in.put((byte)'a');
filled =_parser.parseNext();
assertEquals(2048,filled);
assertEquals(0,_handler._data.size());
assertEquals(0,_handler._utf8.length());
_handler._code=0;
_handler._message=null;
_in.sendMask();
_in.put((byte)0x84);
_in.put((byte)0x7E);
_in.put((byte)(1024>>8));
_in.put((byte)(1024&0xff));
for (int i=0;i<1024;i++)
_in.put((byte)'a');
filled =_parser.parseNext();
assertEquals(1024+8+1,filled);
assertEquals(1,_handler._data.size());
assertEquals(1024,_handler._data.get(0).length());
}
private class Handler implements WebSocketParser.FrameHandler
{
Utf8StringBuilder _utf8 = new Utf8StringBuilder();
public List<String> _data = new ArrayList<String>();
private byte _flags;
private byte _opcode;
int _code;
String _message;
public void onFrame(byte flags, byte opcode, ByteBuffer buffer)
{
_flags=flags;
_opcode=opcode;
if ((flags&0x8)==0)
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
else if (_utf8.length()==0)
_data.add(buffer.toString("utf-8"));
else
{
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
_data.add(_utf8.toString());
_utf8.reset();
}
}
public void close(int code,String message)
{
_code=code;
_message=message;
}
}
}

View File

@ -1,369 +0,0 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* 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 static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.io.BufferCache.ByteBuffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.junit.Before;
import org.junit.Test;
public class WebSocketParserD08Test
{
private MaskedByteArrayBuffer _in;
private Handler _handler;
private WebSocketParserD08 _parser;
private byte[] _mask = new byte[] {(byte)0x00,(byte)0xF0,(byte)0x0F,(byte)0xFF};
private int _m;
class MaskedByteArrayBuffer extends ByteArrayBuffer
{
MaskedByteArrayBuffer()
{
super(4096);
}
public void sendMask()
{
super.poke(putIndex(),_mask,0,4);
super.setPutIndex(putIndex()+4);
_m=0;
}
@Override
public int put(ByteBuffer src)
{
return put(src.asArray(),0,src.length());
}
public void putUnmasked(byte b)
{
super.put(b);
}
@Override
public void put(byte b)
{
super.put((byte)(b^_mask[_m++%4]));
}
@Override
public int put(byte[] b, int offset, int length)
{
byte[] mb = new byte[b.length];
final int end=offset+length;
for (int i=offset;i<end;i++)
{
mb[i]=(byte)(b[i]^_mask[_m++%4]);
}
return super.put(mb,offset,length);
}
@Override
public int put(byte[] b)
{
return put(b,0,b.length);
}
};
@Before
public void setUp() throws Exception
{
WebSocketBuffers buffers = new WebSocketBuffers(1024);
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
endPoint.setNonBlocking(true);
_handler = new Handler();
_parser=new WebSocketParserD08(buffers, endPoint,_handler,true);
_parser.setFakeFragments(false);
_in = new MaskedByteArrayBuffer();
endPoint.setIn(_in);
}
@Test
public void testCache() throws Exception
{
assertEquals(HttpHeaderValue.UPGRADE_ORDINAL ,((CachedBuffer)HttpHeaderValue.CACHE.lookup("Upgrade")).getOrdinal());
}
@Test
public void testFlagsOppcode() throws Exception
{
_in.putUnmasked((byte)0xff);
_in.putUnmasked((byte)0x80);
_in.sendMask();
int progress =_parser.parseNext();
assertTrue(progress>0);
assertEquals(0xf,_handler._flags);
assertEquals(0xf,_handler._opcode);
assertTrue(_parser.isBufferEmpty());
_parser.returnBuffer();
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testShortText() throws Exception
{
_in.putUnmasked((byte)0x81);
_in.putUnmasked((byte)(0x80|11));
_in.sendMask();
_in.put("Hello World".getBytes(StringUtil.__UTF8));
// System.err.println("tosend="+TypeUtil.toHexString(_in.asArray()));
int progress =_parser.parseNext();
assertEquals(18,progress);
assertEquals("Hello World",_handler._data.get(0));
assertEquals(0x8,_handler._flags);
assertEquals(0x1,_handler._opcode);
assertTrue(_parser.isBufferEmpty());
_parser.returnBuffer();
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testShortUtf8() throws Exception
{
String string = "Hell\uFF4f W\uFF4Frld";
byte[] bytes = string.getBytes("UTF-8");
_in.putUnmasked((byte)0x81);
_in.putUnmasked((byte)(0x80|bytes.length));
_in.sendMask();
_in.put(bytes);
int progress =_parser.parseNext();
assertEquals(bytes.length+7,progress);
assertEquals(string,_handler._data.get(0));
assertEquals(0x8,_handler._flags);
assertEquals(0x1,_handler._opcode);
_parser.returnBuffer();
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testMediumText() throws Exception
{
String string = "Hell\uFF4f Medium W\uFF4Frld ";
for (int i=0;i<4;i++)
string = string+string;
string += ". The end.";
byte[] bytes = string.getBytes(StringUtil.__UTF8);
_in.putUnmasked((byte)0x81);
_in.putUnmasked((byte)(0x80|0x7E));
_in.putUnmasked((byte)(bytes.length>>8));
_in.putUnmasked((byte)(bytes.length&0xff));
_in.sendMask();
_in.put(bytes);
int progress =_parser.parseNext();
assertEquals(bytes.length+9,progress);
assertEquals(string,_handler._data.get(0));
assertEquals(0x8,_handler._flags);
assertEquals(0x1,_handler._opcode);
_parser.returnBuffer();
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testLongText() throws Exception
{
WebSocketBuffers buffers = new WebSocketBuffers(0x20000);
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
WebSocketParserD08 parser=new WebSocketParserD08(buffers, endPoint,_handler,false);
ByteArrayBuffer in = new ByteArrayBuffer(0x20000);
endPoint.setIn(in);
String string = "Hell\uFF4f Big W\uFF4Frld ";
for (int i=0;i<12;i++)
string = string+string;
string += ". The end.";
byte[] bytes = string.getBytes("UTF-8");
_in.sendMask();
in.put((byte)0x84);
in.put((byte)0x7F);
in.put((byte)0x00);
in.put((byte)0x00);
in.put((byte)0x00);
in.put((byte)0x00);
in.put((byte)0x00);
in.put((byte)(bytes.length>>16));
in.put((byte)((bytes.length>>8)&0xff));
in.put((byte)(bytes.length&0xff));
in.put(bytes);
int progress =parser.parseNext();
parser.returnBuffer();
assertEquals(bytes.length+11,progress);
assertEquals(string,_handler._data.get(0));
assertTrue(parser.isBufferEmpty());
assertTrue(parser.getBuffer()==null);
}
@Test
public void testShortFragmentTest() throws Exception
{
_in.putUnmasked((byte)0x01);
_in.putUnmasked((byte)0x86);
_in.sendMask();
_in.put("Hello ".getBytes(StringUtil.__UTF8));
_in.putUnmasked((byte)0x80);
_in.putUnmasked((byte)0x85);
_in.sendMask();
_in.put("World".getBytes(StringUtil.__UTF8));
int progress =_parser.parseNext();
assertEquals(24,progress);
assertEquals(0,_handler._data.size());
assertFalse(_parser.isBufferEmpty());
assertFalse(_parser.getBuffer()==null);
progress =_parser.parseNext();
_parser.returnBuffer();
assertEquals(1,progress);
assertEquals("Hello World",_handler._data.get(0));
assertTrue(_parser.isBufferEmpty());
assertTrue(_parser.getBuffer()==null);
}
@Test
public void testFrameTooLarge() throws Exception
{
// Buffers are only 1024, so this frame is too large
_parser.setFakeFragments(false);
_in.putUnmasked((byte)0x81);
_in.putUnmasked((byte)(0x80|0x7E));
_in.putUnmasked((byte)(2048>>8));
_in.putUnmasked((byte)(2048&0xff));
_in.sendMask();
int progress =_parser.parseNext();
assertTrue(progress>0);
assertEquals(WebSocketConnectionD08.CLOSE_BADDATA,_handler._code);
for (int i=0;i<2048;i++)
_in.put((byte)'a');
progress =_parser.parseNext();
assertEquals(2048,progress);
assertEquals(0,_handler._data.size());
assertEquals(0,_handler._utf8.length());
_handler._code=0;
_handler._message=null;
_in.putUnmasked((byte)0x81);
_in.putUnmasked((byte)0xFE);
_in.putUnmasked((byte)(1024>>8));
_in.putUnmasked((byte)(1024&0xff));
_in.sendMask();
for (int i=0;i<1024;i++)
_in.put((byte)'a');
progress =_parser.parseNext();
assertTrue(progress>0);
assertEquals(1,_handler._data.size());
assertEquals(1024,_handler._data.get(0).length());
}
@Test
public void testFakeFragement() throws Exception
{
// Buffers are only 1024, so this frame will be fake fragmented
_parser.setFakeFragments(true);
_in.putUnmasked((byte)0x81);
_in.putUnmasked((byte)(0x80|0x7E));
_in.putUnmasked((byte)(2048>>8));
_in.putUnmasked((byte)(2048&0xff));
_in.sendMask();
for (int i=0;i<2048;i++)
_in.put((byte)('a'+i%26));
int progress =_parser.parseNext();
assertTrue(progress>0);
assertEquals(2,_handler._frames);
assertEquals(WebSocketConnectionD08.OP_CONTINUATION,_handler._opcode);
assertEquals(1,_handler._data.size());
String mesg=_handler._data.remove(0);
assertEquals(2048,mesg.length());
for (int i=0;i<2048;i++)
assertEquals(('a'+i%26),mesg.charAt(i));
}
private class Handler implements WebSocketParser.FrameHandler
{
Utf8StringBuilder _utf8 = new Utf8StringBuilder();
public List<String> _data = new ArrayList<String>();
private byte _flags;
private byte _opcode;
int _code;
String _message;
int _frames;
public void onFrame(byte flags, byte opcode, ByteBuffer buffer)
{
_frames++;
_flags=flags;
_opcode=opcode;
if ((flags&0x8)==0)
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
else if (_utf8.length()==0)
_data.add(buffer.toString("utf-8"));
else
{
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
_data.add(_utf8.toString());
_utf8.reset();
}
}
public void close(int code,String message)
{
_code=code;
_message=message;
}
}
}