324369 Implement draft-ietf-hybi-thewebsocketprotocol-01

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2245 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Greg Wilkins 2010-09-03 07:05:53 +00:00
parent f3c64a9c26
commit 1596451908
14 changed files with 87 additions and 33 deletions

View File

@ -94,6 +94,10 @@ public class WebSocketUpgradeTest extends TestCase
public void onMessage(byte frame, byte[] data, int offset, int length) public void onMessage(byte frame, byte[] data, int offset, int length)
{ {
} }
public void onFragment(boolean more, byte opcode, byte[] data, int offset, int length)
{
}
}; };
@ -245,5 +249,9 @@ public class WebSocketUpgradeTest extends TestCase
{ {
_outbound.sendMessage(msg); _outbound.sendMessage(msg);
} }
public void onFragment(boolean more, byte opcode, byte[] data, int offset, int length)
{
}
} }
} }

View File

@ -7,16 +7,17 @@ public interface WebSocket
public final byte LENGTH_FRAME=(byte)0x80; public final byte LENGTH_FRAME=(byte)0x80;
public final byte SENTINEL_FRAME=(byte)0x00; public final byte SENTINEL_FRAME=(byte)0x00;
void onConnect(Outbound outbound); void onConnect(Outbound outbound);
void onMessage(byte frame,String data); void onMessage(byte opcode,String data);
void onMessage(byte frame,byte[] data, int offset, int length); void onFragment(boolean more,byte opcode,byte[] data, int offset, int length);
void onMessage(byte opcode,byte[] data, int offset, int length);
void onDisconnect(); void onDisconnect();
public interface Outbound public interface Outbound
{ {
void sendMessage(String data) throws IOException; void sendMessage(String data) throws IOException;
void sendMessage(byte frame,String data) throws IOException; void sendMessage(byte opcode,String data) throws IOException;
void sendMessage(byte frame,byte[] data) throws IOException; void sendMessage(byte opcode,byte[] data, int offset, int length) throws IOException;
void sendMessage(byte frame,byte[] data, int offset, int length) throws IOException; void sendFragment(boolean more,byte opcode,byte[] data, int offset, int length) throws IOException;
void disconnect(); void disconnect();
boolean isOpen(); boolean isOpen();
} }

View File

@ -3,10 +3,7 @@ package org.eclipse.jetty.websocket;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.zip.Checksum;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.security.Credential.MD5;
import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.ByteArrayBuffer;
@ -14,10 +11,8 @@ import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.IndirectNIOBuffer; import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.thread.Timeout;
public class WebSocketConnection implements Connection, WebSocket.Outbound public class WebSocketConnection implements Connection, WebSocket.Outbound
{ {
@ -51,6 +46,7 @@ public class WebSocketConnection implements Connection, WebSocket.Outbound
_websocket = websocket; _websocket = websocket;
final WebSocketParser.FrameHandler handler = new WebSocketParser.FrameHandler() final WebSocketParser.FrameHandler handler = new WebSocketParser.FrameHandler()
{ {
boolean _fragmented=false;
Utf8StringBuilder _utf8 = new Utf8StringBuilder(); Utf8StringBuilder _utf8 = new Utf8StringBuilder();
public void onFrame(boolean more, byte flags, byte opcode, Buffer buffer) public void onFrame(boolean more, byte flags, byte opcode, Buffer buffer)
@ -62,21 +58,36 @@ public class WebSocketConnection implements Connection, WebSocket.Outbound
if (opcode==0) if (opcode==0)
{ {
if (more) if (more)
{
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length()); _utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
else if (_utf8.length()==0) _fragmented=true;
_websocket.onMessage(opcode,buffer.toString("utf-8")); }
else else if (_fragmented)
{ {
_utf8.append(buffer.array(),buffer.getIndex(),buffer.length()); _utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
_websocket.onMessage(opcode,_utf8.toString()); _websocket.onMessage(opcode,_utf8.toString());
_utf8.reset(); _utf8.reset();
_fragmented=false;
}
else
{
_websocket.onMessage(opcode,buffer.toString("utf-8"));
} }
} }
else else
{ {
if (more) if (more)
throw new IllegalStateException("not implemented"); {
_websocket.onMessage(opcode,array,buffer.getIndex(),buffer.length()); _websocket.onFragment(true,opcode,array,buffer.getIndex(),buffer.length());
}
else if (_fragmented)
{
_websocket.onFragment(false,opcode,array,buffer.getIndex(),buffer.length());
}
else
{
_websocket.onMessage(opcode,array,buffer.getIndex(),buffer.length());
}
} }
} }
catch(ThreadDeath th) catch(ThreadDeath th)
@ -262,14 +273,17 @@ public class WebSocketConnection implements Connection, WebSocket.Outbound
_idle.access(_endp); _idle.access(_endp);
} }
public void sendMessage(byte frame, byte[] content) throws IOException public void sendMessage(byte opcode, byte[] content, int offset, int length) throws IOException
{ {
sendMessage(frame, content, 0, content.length); _generator.addFrame(opcode,content,offset,length,_endp.getMaxIdleTime());
_generator.flush();
checkWriteable();
_idle.access(_endp);
} }
public void sendMessage(byte frame, byte[] content, int offset, int length) throws IOException public void sendFragment(boolean more,byte opcode, byte[] content, int offset, int length) throws IOException
{ {
_generator.addFrame(frame,content,offset,length,_endp.getMaxIdleTime()); _generator.addFragment(more,opcode,content,offset,length,_endp.getMaxIdleTime());
_generator.flush(); _generator.flush();
checkWriteable(); checkWriteable();
_idle.access(_endp); _idle.access(_endp);

View File

@ -13,6 +13,6 @@ public interface WebSocketGenerator
boolean isBufferEmpty(); boolean isBufferEmpty();
void addFrame(byte opcode, String content, int maxIdleTime) throws IOException; void addFrame(byte opcode, String content, int maxIdleTime) throws IOException;
void addFrame(byte opcode, byte[] content, int offset, int length, int maxIdleTime) throws IOException; void addFrame(byte opcode, byte[] content, int offset, int length, int maxIdleTime) throws IOException;
void addFrame(byte opcode, byte[] content, int maxIdleTime)throws IOException; void addFragment(boolean more,byte opcode, byte[] content, int offset, int length, int maxIdleTime) throws IOException;
int flush(int maxIdleTime) throws IOException; int flush(int maxIdleTime) throws IOException;
} }

View File

@ -28,12 +28,7 @@ public class WebSocketGeneratorD00 implements WebSocketGenerator
_buffers=buffers; _buffers=buffers;
_endp=endp; _endp=endp;
} }
public synchronized void addFrame(byte frame,byte[] content, int blockFor) throws IOException
{
addFrame(frame,content,0,content.length,blockFor);
}
public synchronized void addFrame(byte opcode,byte[] content, int offset, int length, int blockFor) throws IOException public synchronized void addFrame(byte opcode,byte[] content, int offset, int length, int blockFor) throws IOException
{ {
if (_buffer==null) if (_buffer==null)
@ -164,5 +159,12 @@ public class WebSocketGeneratorD00 implements WebSocketGenerator
{ {
return _buffer==null || _buffer.length()==0; return _buffer==null || _buffer.length()==0;
} }
public void addFragment(boolean more, byte opcode, byte[] content, int offset, int length, int maxIdleTime) throws IOException
{
if (more)
throw new UnsupportedOperationException("fragmented");
addFrame(opcode,content,offset,length,maxIdleTime);
}
} }

View File

@ -35,8 +35,14 @@ public class WebSocketGeneratorD01 implements WebSocketGenerator
{ {
addFrame(opcode,content,0,content.length,blockFor); addFrame(opcode,content,0,content.length,blockFor);
} }
public synchronized void addFrame(byte opcode,byte[] content, int offset, int length, int blockFor) throws IOException public synchronized void addFrame(byte opcode,byte[] content, int offset, int length, int blockFor) throws IOException
{
addFragment(false,opcode,content,offset,length,blockFor);
}
public synchronized void addFragment(boolean more, byte opcode, byte[] content, int offset, int length, int blockFor) throws IOException
{ {
if (_buffer==null) if (_buffer==null)
_buffer=_buffers.getDirectBuffer(); _buffer=_buffers.getDirectBuffer();
@ -55,6 +61,8 @@ public class WebSocketGeneratorD01 implements WebSocketGenerator
fragment=_buffer.capacity()-10; fragment=_buffer.capacity()-10;
bufferPut((byte)(0x80|opcode), blockFor); bufferPut((byte)(0x80|opcode), blockFor);
} }
else if (more)
bufferPut((byte)(0x80|opcode), blockFor);
else else
bufferPut(opcode, blockFor); bufferPut(opcode, blockFor);
@ -178,4 +186,5 @@ public class WebSocketGeneratorD01 implements WebSocketGenerator
{ {
return _buffer==null || _buffer.length()==0; return _buffer==null || _buffer.length()==0;
} }
} }

View File

@ -56,7 +56,7 @@ public class WebSocketGeneratorD00Test
for (int i=0;i<b.length;i++) for (int i=0;i<b.length;i++)
b[i]=(byte)('0'+(i%10)); b[i]=(byte)('0'+(i%10));
_generator.addFrame((byte)0xf,b,0); _generator.addFrame((byte)0xf,b,0,b.length,0);
_generator.flush(); _generator.flush();
assertEquals(0x0f,_out.get()); assertEquals(0x0f,_out.get());
@ -74,7 +74,7 @@ public class WebSocketGeneratorD00Test
for (int i=0;i<b.length;i++) for (int i=0;i<b.length;i++)
b[i]=(byte)('0'+(i%10)); b[i]=(byte)('0'+(i%10));
_generator.addFrame((byte)0xf,b,0); _generator.addFrame((byte)0xf,b,0,b.length,0);
_generator.flush(); _generator.flush();
assertEquals(0x8f,_out.get()&0xff); assertEquals(0x8f,_out.get()&0xff);

View File

@ -53,7 +53,9 @@ public class WebSocketGeneratorD01Test
@Test @Test
public void testOneBuffer() throws Exception public void testOneBuffer() throws Exception
{ {
_generator.addFrame((byte)0x84,"Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8),0); String string = "Hell\uFF4F W\uFF4Frld";
byte[] bytes=string.getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x84,bytes,0,bytes.length,0);
_generator.flush(); _generator.flush();
assertEquals(0x84,0xff&_out.get()); assertEquals(0x84,0xff&_out.get());
assertEquals(15,0xff&_out.get()); assertEquals(15,0xff&_out.get());
@ -81,7 +83,7 @@ public class WebSocketGeneratorD01Test
for (int i=0;i<b.length;i++) for (int i=0;i<b.length;i++)
b[i]=(byte)('0'+(i%10)); b[i]=(byte)('0'+(i%10));
_generator.addFrame((byte)0x85,b,0); _generator.addFrame((byte)0x85,b,0,b.length,0);
_generator.flush(); _generator.flush();
assertEquals(0x85,0xff&_out.get()); assertEquals(0x85,0xff&_out.get());

View File

@ -126,6 +126,10 @@ public class WebSocketLoadTest
} }
} }
public void onFragment(boolean more, byte opcode, byte[] data, int offset, int length)
{
}
public void onMessage(byte frame, byte[] data, int offset, int length) public void onMessage(byte frame, byte[] data, int offset, int length)
{ {
} }

View File

@ -147,7 +147,7 @@ public class WebSocketMessageTest
for (int i = 0; i < 64 * 1024 / text.length(); ++i) for (int i = 0; i < 64 * 1024 / text.length(); ++i)
message.append(text); message.append(text);
byte[] data = message.toString().getBytes("UTF-8"); byte[] data = message.toString().getBytes("UTF-8");
_serverWebSocket.outbound.sendMessage(WebSocket.LENGTH_FRAME, data); _serverWebSocket.outbound.sendMessage(WebSocket.LENGTH_FRAME, data,0,data.length);
// Length of the message is 65536, so the length will be encoded as 0x84 0x80 0x00 // Length of the message is 65536, so the length will be encoded as 0x84 0x80 0x00
int frame = input.read(); int frame = input.read();
@ -194,5 +194,9 @@ public class WebSocketMessageTest
public void onDisconnect() public void onDisconnect()
{ {
} }
public void onFragment(boolean more, byte opcode, byte[] data, int offset, int length)
{
}
} }
} }

View File

@ -117,7 +117,6 @@ public class WebSocketParserD01Test
string += ". The end."; string += ". The end.";
byte[] bytes = string.getBytes("UTF-8"); byte[] bytes = string.getBytes("UTF-8");
System.err.println(Long.toHexString(bytes.length));
in.put((byte)0x00); in.put((byte)0x00);
in.put((byte)0x7F); in.put((byte)0x7F);

View File

@ -137,7 +137,6 @@ public class WebSocketTest
ByteArrayBuffer out = _connector.getResponses(buffer,true); ByteArrayBuffer out = _connector.getResponses(buffer,true);
String response = StringUtil.printable(out.asArray()); String response = StringUtil.printable(out.asArray());
System.err.println(response);
assertTrue(response.startsWith("HTTP/1.1 101 Web Socket Protocol Handshake")); assertTrue(response.startsWith("HTTP/1.1 101 Web Socket Protocol Handshake"));
assertTrue(response.contains("Upgrade: WebSocket")); assertTrue(response.contains("Upgrade: WebSocket"));
@ -181,5 +180,9 @@ public class WebSocketTest
{ {
_disconnected=true; _disconnected=true;
} }
public void onFragment(boolean more, byte opcode, byte[] data, int offset, int length)
{
}
} }
} }

View File

@ -77,6 +77,10 @@ public class WebSocketTestServer extends Server
{ {
_webSockets.remove(this); _webSockets.remove(this);
} }
public void onFragment(boolean more, byte opcode, byte[] data, int offset, int length)
{
}
} }
public static void main(String[] args) public static void main(String[] args)

View File

@ -74,5 +74,9 @@ public class WebSocketChatServlet extends WebSocketServlet
// Log.info(this+" onDisconnect"); // Log.info(this+" onDisconnect");
_members.remove(this); _members.remove(this);
} }
public void onFragment(boolean more, byte opcode, byte[] data, int offset, int length)
{
}
} }
} }