WebSocketGenerator
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1041 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
bb1bb0b960
commit
c2f081ae8c
|
@ -1,18 +1,208 @@
|
|||
package org.eclipse.jetty.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jetty.io.Buffer;
|
||||
import org.eclipse.jetty.io.Buffers;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** 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 WebSocketGenerator
|
||||
{
|
||||
|
||||
public int flush()
|
||||
final private Buffers _buffers;
|
||||
final private EndPoint _endp;
|
||||
private Buffer _buffer;
|
||||
|
||||
public WebSocketGenerator(Buffers buffers, EndPoint endp)
|
||||
{
|
||||
_buffers=buffers;
|
||||
_endp=endp;
|
||||
}
|
||||
|
||||
synchronized public boolean addMessage(byte frame,Buffer content, long blockFor) throws IOException
|
||||
{
|
||||
if (_buffer==null)
|
||||
_buffer=_buffers.getBuffer();
|
||||
else if (_buffer.length()>0)
|
||||
flushBuffer();
|
||||
|
||||
int length=content.length();
|
||||
if (length>2097152)
|
||||
throw new IllegalArgumentException("too big");
|
||||
|
||||
int length_bytes=(length>16384)?3:(length>128)?2:1;
|
||||
|
||||
if (_buffer.space()<length+1+length_bytes)
|
||||
{
|
||||
// TODO block if there can be space
|
||||
throw new IllegalArgumentException("no space");
|
||||
}
|
||||
|
||||
_buffer.put((byte)(0x80|frame));
|
||||
|
||||
switch (length_bytes)
|
||||
{
|
||||
case 3:
|
||||
_buffer.put((byte)(0x80|(length>>14)));
|
||||
case 2:
|
||||
_buffer.put((byte)(0x80|(0x7f&(length>>7))));
|
||||
case 1:
|
||||
_buffer.put((byte)(0x7f&length));
|
||||
}
|
||||
|
||||
_buffer.put(content);
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized public boolean addMessage(byte frame, String content, long blockFor) throws IOException
|
||||
{
|
||||
if (_buffer==null)
|
||||
_buffer=_buffers.getBuffer();
|
||||
else if (_buffer.length()>0)
|
||||
flushBuffer();
|
||||
|
||||
int length=content.length();
|
||||
int space=waitForSpace(length+2,blockFor);
|
||||
|
||||
_buffer.put((byte)(0x7f&frame));
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
int code = content.charAt(i);
|
||||
|
||||
if ((code & 0xffffff80) == 0)
|
||||
{
|
||||
// 1b
|
||||
if (space<1)
|
||||
space=waitForSpace(1,blockFor);
|
||||
_buffer.put((byte)(code));
|
||||
space--;
|
||||
}
|
||||
else if((code&0xfffff800)==0)
|
||||
{
|
||||
// 2b
|
||||
if (space<2)
|
||||
space=waitForSpace(2,blockFor);
|
||||
_buffer.put((byte)(0xc0|(code>>6)));
|
||||
_buffer.put((byte)(0x80|(code&0x3f)));
|
||||
space-=2;
|
||||
}
|
||||
else if((code&0xffff0000)==0)
|
||||
{
|
||||
// 3b
|
||||
if (space<3)
|
||||
space=waitForSpace(3,blockFor);
|
||||
_buffer.put((byte)(0xe0|(code>>12)));
|
||||
_buffer.put((byte)(0x80|((code>>6)&0x3f)));
|
||||
_buffer.put((byte)(0x80|(code&0x3f)));
|
||||
space-=3;
|
||||
}
|
||||
else if((code&0xff200000)==0)
|
||||
{
|
||||
// 4b
|
||||
if (space<4)
|
||||
space=waitForSpace(4,blockFor);
|
||||
_buffer.put((byte)(0xf0|(code>>18)));
|
||||
_buffer.put((byte)(0x80|((code>>12)&0x3f)));
|
||||
_buffer.put((byte)(0x80|((code>>6)&0x3f)));
|
||||
_buffer.put((byte)(0x80|(code&0x3f)));
|
||||
space-=4;
|
||||
}
|
||||
else if((code&0xf4000000)==0)
|
||||
{
|
||||
// 5b
|
||||
if (space<5)
|
||||
space=waitForSpace(5,blockFor);
|
||||
_buffer.put((byte)(0xf8|(code>>24)));
|
||||
_buffer.put((byte)(0x80|((code>>18)&0x3f)));
|
||||
_buffer.put((byte)(0x80|((code>>12)&0x3f)));
|
||||
_buffer.put((byte)(0x80|((code>>6)&0x3f)));
|
||||
_buffer.put((byte)(0x80|(code&0x3f)));
|
||||
space-=5;
|
||||
}
|
||||
else if((code&0x80000000)==0)
|
||||
{
|
||||
// 6b
|
||||
if (space<6)
|
||||
space=waitForSpace(6,blockFor);
|
||||
_buffer.put((byte)(0xfc|(code>>30)));
|
||||
_buffer.put((byte)(0x80|((code>>24)&0x3f)));
|
||||
_buffer.put((byte)(0x80|((code>>18)&0x3f)));
|
||||
_buffer.put((byte)(0x80|((code>>12)&0x3f)));
|
||||
_buffer.put((byte)(0x80|((code>>6)&0x3f)));
|
||||
_buffer.put((byte)(0x80|(code&0x3f)));
|
||||
space-=6;
|
||||
}
|
||||
else
|
||||
{
|
||||
_buffer.put((byte)('?'));
|
||||
space-=1;
|
||||
}
|
||||
}
|
||||
|
||||
if (space<1)
|
||||
space=waitForSpace(1,blockFor);
|
||||
_buffer.put((byte)(0xff));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int waitForSpace(int needed, long blockFor)
|
||||
{
|
||||
int space=_buffer.space();
|
||||
|
||||
if (space<needed)
|
||||
{
|
||||
_buffer.compact();
|
||||
space=_buffer.space();
|
||||
if (space<needed)
|
||||
// TODO flush and wait for space
|
||||
throw new IllegalStateException("no space");
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
synchronized public int flush(long blockFor)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isBufferEmpty()
|
||||
synchronized public int flush() throws IOException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
int flushed = flushBuffer();
|
||||
if (_buffer.length()==0)
|
||||
{
|
||||
_buffers.returnBuffer(_buffer);
|
||||
_buffer=null;
|
||||
}
|
||||
return flushed;
|
||||
}
|
||||
|
||||
private int flushBuffer() throws IOException
|
||||
{
|
||||
if (!_endp.isOpen())
|
||||
return -1;
|
||||
|
||||
if (_buffer!=null)
|
||||
{
|
||||
int flushed =_endp.flush(_buffer);
|
||||
if (flushed>0)
|
||||
_buffer.skip(flushed);
|
||||
return flushed;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
synchronized public boolean isBufferEmpty()
|
||||
{
|
||||
return _buffer==null || _buffer.length()==0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package org.eclipse.jetty.websocket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.io.Buffer;
|
||||
import org.eclipse.jetty.io.Buffers;
|
||||
import org.eclipse.jetty.io.ByteArrayBuffer;
|
||||
import org.eclipse.jetty.io.ByteArrayEndPoint;
|
||||
import org.eclipse.jetty.io.SimpleBuffers;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public class WebSocketGeneratorTest extends TestCase
|
||||
{
|
||||
|
||||
Buffers _buffers;
|
||||
ByteArrayBuffer _out;
|
||||
ByteArrayEndPoint _endp;
|
||||
WebSocketGenerator _generator;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
_buffers=new SimpleBuffers(null,new ByteArrayBuffer(1024));
|
||||
_endp = new ByteArrayEndPoint();
|
||||
_generator = new WebSocketGenerator(_buffers,_endp);
|
||||
_out = new ByteArrayBuffer(2048);
|
||||
_endp.setOut(_out);
|
||||
}
|
||||
|
||||
|
||||
public void testOneString() throws Exception
|
||||
{
|
||||
_generator.addMessage((byte)0x04,"Hell\uFF4F W\uFF4Frld",0);
|
||||
_generator.flush();
|
||||
assertEquals(4,_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(0xff,0xff&_out.get());
|
||||
}
|
||||
|
||||
public void testOneBuffer() throws Exception
|
||||
{
|
||||
_generator.addMessage((byte)0x04,new ByteArrayBuffer("Hell\uFF4F W\uFF4Frld",StringUtil.__UTF8),0);
|
||||
_generator.flush();
|
||||
assertEquals(0x84,0xff&_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());
|
||||
}
|
||||
|
||||
public void testOneLongBuffer() throws Exception
|
||||
{
|
||||
byte[] b=new byte[150];
|
||||
for (int i=0;i<b.length;i++)
|
||||
b[i]=(byte)('0'+(i%10));
|
||||
|
||||
|
||||
_generator.addMessage((byte)0x05,new ByteArrayBuffer(b),0);
|
||||
|
||||
_generator.flush();
|
||||
assertEquals(0x85,0xff&_out.get());
|
||||
assertEquals(0x80|(b.length>>7),0xff&_out.get());
|
||||
assertEquals(0x7f&b.length,0xff&_out.get());
|
||||
for (int i=0;i<b.length;i++)
|
||||
assertEquals('0'+(i%10),0xff&_out.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -12,6 +12,10 @@ import org.eclipse.jetty.util.StringUtil;
|
|||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public class WebSocketParserTest extends TestCase
|
||||
{
|
||||
|
||||
|
|
Loading…
Reference in New Issue