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:
parent
f3c64a9c26
commit
1596451908
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue