diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGenerator.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGenerator.java index 83647e34d5e..d4d89ca0550 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGenerator.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGenerator.java @@ -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()>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 (space0) + _buffer.skip(flushed); + return flushed; + } + return 0; + } + + synchronized public boolean isBufferEmpty() + { + return _buffer==null || _buffer.length()==0; } } diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorTest.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorTest.java new file mode 100644 index 00000000000..e88561dc267 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorTest.java @@ -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>7),0xff&_out.get()); + assertEquals(0x7f&b.length,0xff&_out.get()); + for (int i=0;i