some frame consolidation work in progress

This commit is contained in:
Jesse McConnell 2012-06-21 16:22:17 -05:00
parent aaf27d7e2a
commit f4a644347b
16 changed files with 158 additions and 142 deletions

View File

@ -5,7 +5,6 @@ import java.util.Map;
import org.eclipse.jetty.websocket.frames.BinaryFrame; import org.eclipse.jetty.websocket.frames.BinaryFrame;
import org.eclipse.jetty.websocket.frames.CloseFrame; import org.eclipse.jetty.websocket.frames.CloseFrame;
import org.eclipse.jetty.websocket.frames.ContinuationFrame;
import org.eclipse.jetty.websocket.frames.PingFrame; import org.eclipse.jetty.websocket.frames.PingFrame;
import org.eclipse.jetty.websocket.frames.PongFrame; import org.eclipse.jetty.websocket.frames.PongFrame;
import org.eclipse.jetty.websocket.frames.TextFrame; import org.eclipse.jetty.websocket.frames.TextFrame;

View File

@ -37,8 +37,6 @@ public class BaseFrame
private int payloadLength; private int payloadLength;
private byte mask[]; private byte mask[];
// internal tracking
private int continuationIndex = 0;
/** /**
* Default constructor * Default constructor
@ -55,19 +53,7 @@ public class BaseFrame
this.opcode = opcode; this.opcode = opcode;
} }
/**
* The number of fragments this frame consists of.
* <p>
* For every {@link OpCode#CONTINUATION} opcode encountered, this increments by one.
* <p>
* Note: Not part of the Base Framing Protocol / header information.
*
* @return the number of continuation fragments encountered.
*/
public int getContinuationIndex()
{
return continuationIndex;
}
public byte[] getMask() public byte[] getMask()
{ {
@ -128,14 +114,9 @@ public class BaseFrame
masked = false; masked = false;
payloadLength = -1; payloadLength = -1;
mask = null; mask = null;
continuationIndex = 0;
}
public void setContinuationIndex(int continuationIndex)
{
this.continuationIndex = continuationIndex;
} }
public void setFin(boolean fin) public void setFin(boolean fin)
{ {
this.fin = fin; this.fin = fin;

View File

@ -10,7 +10,6 @@ import org.eclipse.jetty.websocket.api.OpCode;
*/ */
public class BinaryFrame extends DataFrame public class BinaryFrame extends DataFrame
{ {
private ByteBuffer data; // TODO: make this a standard byte buffer?
/** /**
* Default unspecified data * Default unspecified data
@ -18,17 +17,7 @@ public class BinaryFrame extends DataFrame
public BinaryFrame() public BinaryFrame()
{ {
super(OpCode.BINARY); super(OpCode.BINARY);
} }
/**
* Get the data
*
* @return the raw bytebuffer data (can be null)
*/
public ByteBuffer getData()
{
return data;
}
@Override @Override
public OpCode getOpCode() public OpCode getOpCode()
@ -36,29 +25,14 @@ public class BinaryFrame extends DataFrame
return OpCode.BINARY; return OpCode.BINARY;
} }
/**
* Set the data and payload length.
*
* @param buf @Override
* the bytebuffer to set public void setPayload(ByteBuffer buffer)
*/
public void setData(byte buf[])
{ {
int len = buf.length; // TODO Auto-generated method stub
this.data = ByteBuffer.allocate(len); super.setPayload(buffer);
this.setPayloadLength(len);
}
/**
* Set the data and payload length.
*
* @param buf
* the byte array to set
*/
public void setData(ByteBuffer buffer)
{
this.data = buffer;
this.setPayloadLength(buffer.capacity());
} }
@Override @Override
@ -67,7 +41,7 @@ public class BinaryFrame extends DataFrame
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append("BinaryFrame["); b.append("BinaryFrame[");
b.append("len=").append(getPayloadLength()); b.append("len=").append(getPayloadLength());
b.append(",data=").append(BufferUtil.toDetailString(getData())); b.append(",data=").append(BufferUtil.toDetailString(getPayload()));
b.append("]"); b.append("]");
return b.toString(); return b.toString();
} }

View File

@ -1,11 +0,0 @@
package org.eclipse.jetty.websocket.frames;
import org.eclipse.jetty.websocket.api.OpCode;
public class ContinuationFrame extends BaseFrame
{
public ContinuationFrame()
{
super(OpCode.CONTINUATION);
}
}

View File

@ -1,16 +1,97 @@
package org.eclipse.jetty.websocket.frames; package org.eclipse.jetty.websocket.frames;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.OpCode; import org.eclipse.jetty.websocket.api.OpCode;
public abstract class DataFrame extends BaseFrame public abstract class DataFrame extends BaseFrame
{ {
// internal tracking
private int continuationIndex = 0;
private boolean continuation = false;
private ByteBuffer payload;
public DataFrame() public DataFrame()
{ {
super(); super();
} }
public DataFrame(OpCode opcode) public DataFrame(OpCode opcode)
{ {
super(opcode); super(opcode);
} }
/**
* Get the data
*
* @return the raw bytebuffer data (can be null)
*/
public ByteBuffer getPayload()
{
return payload;
}
/**
* Set the data and payload length.
*
* @param buf
* the bytebuffer to set
*/
protected void setPayload(byte buf[])
{
int len = buf.length;
this.payload = ByteBuffer.allocate(len);
this.setPayloadLength(len);
}
/**
* Set the data and payload length.
*
* @param buf
* the byte array to set
*/
protected void setPayload(ByteBuffer buffer)
{
this.payload = buffer;
this.setPayloadLength(buffer.capacity());
}
/**
* The number of fragments this frame consists of.
* <p>
* For every {@link OpCode#CONTINUATION} opcode encountered, this increments by one.
* <p>
* Note: Not part of the Base Framing Protocol / header information.
*
* @return the number of continuation fragments encountered.
*/
public int getContinuationIndex()
{
return continuationIndex;
}
public void setContinuationIndex(int continuationIndex)
{
this.continuationIndex = continuationIndex;
}
@Override
public void reset()
{
super.reset();
continuationIndex = 0;
}
public boolean isContinuation()
{
return continuation;
}
public void setContinuation(boolean continuation)
{
this.continuation = continuation;
}
} }

View File

@ -1,5 +1,7 @@
package org.eclipse.jetty.websocket.frames; package org.eclipse.jetty.websocket.frames;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.OpCode; import org.eclipse.jetty.websocket.api.OpCode;
/** /**
@ -7,8 +9,6 @@ import org.eclipse.jetty.websocket.api.OpCode;
*/ */
public class TextFrame extends DataFrame public class TextFrame extends DataFrame
{ {
private StringBuilder data = new StringBuilder();
/** /**
* Default constructor (unspecified data) * Default constructor (unspecified data)
*/ */
@ -26,17 +26,7 @@ public class TextFrame extends DataFrame
public TextFrame(String message) public TextFrame(String message)
{ {
this(); this();
setData(message); setPayload(message);
}
/**
* Get the data
*
* @return the raw StringBuilder data (can be null)
*/
public StringBuilder getData()
{
return data;
} }
/** /**
@ -45,24 +35,18 @@ public class TextFrame extends DataFrame
* @param str * @param str
* the String to set * the String to set
*/ */
public void setData(String str) public void setPayload(String str)
{ {
int len = str.length(); int len = str.length();
this.data = new StringBuilder(str); ByteBuffer b = ByteBuffer.allocate(len);
b.put(str.getBytes()); // TODO validate utf-8
this.setPayload(b);
this.setPayloadLength(len); this.setPayloadLength(len);
} }
/** public String getPayloadAsText()
* Set the data and payload length.
*
* @param str
* the StringBuilder to set
*/
public void setData(StringBuilder str)
{ {
int len = str.length(); return new String(getPayload().array());
this.data = str;
this.setPayloadLength(len);
} }
@Override @Override
@ -71,7 +55,7 @@ public class TextFrame extends DataFrame
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append("TextFrame["); b.append("TextFrame[");
b.append("len=").append(getPayloadLength()); b.append("len=").append(getPayloadLength());
b.append(",data=").append(data); b.append(",data=").append(getPayload()); // TODO pp
b.append("]"); b.append("]");
return b.toString(); return b.toString();
} }

View File

@ -16,6 +16,6 @@ public class BinaryFrameGenerator extends FrameGenerator<BinaryFrame>
@Override @Override
public ByteBuffer payload(BinaryFrame binary) public ByteBuffer payload(BinaryFrame binary)
{ {
return binary.getData(); return binary.getPayload();
} }
} }

View File

@ -3,9 +3,11 @@ package org.eclipse.jetty.websocket.generator;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.PolicyViolationException; import org.eclipse.jetty.websocket.api.PolicyViolationException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.frames.BaseFrame; import org.eclipse.jetty.websocket.frames.BaseFrame;
import org.eclipse.jetty.websocket.frames.DataFrame;
public abstract class FrameGenerator<T extends BaseFrame> public abstract class FrameGenerator<T extends BaseFrame>
{ {
@ -48,7 +50,24 @@ public abstract class FrameGenerator<T extends BaseFrame>
// TODO: extensions can negotiate this (somehow) // TODO: extensions can negotiate this (somehow)
throw new PolicyViolationException("RSV3 not allowed to be set"); throw new PolicyViolationException("RSV3 not allowed to be set");
} }
b |= (frame.getOpCode().getCode() & 0x0F);
// TODO ewe
if ( frame instanceof DataFrame)
{
if ( ((DataFrame)frame).isContinuation() )
{
b |= OpCode.CONTINUATION.getCode() & 0x0F;
}
else
{
b |= (frame.getOpCode().getCode() & 0x0F);
}
}
else
{
b |= (frame.getOpCode().getCode() & 0x0F);
}
framing.put(b); framing.put(b);
// is masked // is masked

View File

@ -17,19 +17,7 @@ public class TextFrameGenerator extends FrameGenerator<TextFrame>
@Override @Override
public ByteBuffer payload(TextFrame text) public ByteBuffer payload(TextFrame text)
{ {
try return text.getPayload();
{
String data = text.getData().toString();
ByteBuffer payload = ByteBuffer.allocate(data.length());
payload.put(data.getBytes("UTF-8"));
return payload;
}
catch (UnsupportedEncodingException e)
{
// TODO improve ex handling
throw new WebSocketException("text frame was not correctly encoded");
}
} }
} }

View File

@ -49,7 +49,7 @@ public class BinaryPayloadParser extends FrameParser<BinaryFrame>
if (payload.position() >= payloadLength) if (payload.position() >= payloadLength)
{ {
frame.setData(payload); frame.setPayload(payload);
return true; return true;
} }
} }

View File

@ -12,6 +12,7 @@ import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.WebSocketException; import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.frames.BaseFrame; import org.eclipse.jetty.websocket.frames.BaseFrame;
import org.eclipse.jetty.websocket.frames.DataFrame;
/** /**
* Parsing of a frames in WebSocket land. * Parsing of a frames in WebSocket land.
@ -139,7 +140,11 @@ public class Parser
} }
parser.reset(); parser.reset();
parser.initFrame(fin,rsv1,rsv2,rsv3,opcode); parser.initFrame(fin,rsv1,rsv2,rsv3,opcode);
parser.getFrame().setContinuationIndex(currentContinuationIndex);
if ( parser.getFrame() instanceof DataFrame )
{
((DataFrame)parser.getFrame()).setContinuationIndex(currentContinuationIndex);
}
state = State.BASE_FRAMING; state = State.BASE_FRAMING;
break; break;

View File

@ -49,7 +49,7 @@ public class TextPayloadParser extends FrameParser<TextFrame>
if (payload.position() >= payloadLength) if (payload.position() >= payloadLength)
{ {
payload.flip(); payload.flip();
frame.setData(BufferUtil.toString(payload,StringUtil.__UTF8_CHARSET)); frame.setPayload(BufferUtil.toString(payload,StringUtil.__UTF8_CHARSET));
return true; return true;
} }
} }

View File

@ -47,7 +47,7 @@ public class GeneratorParserRoundtripTest
capture.assertHasFrame(TextFrame.class,1); capture.assertHasFrame(TextFrame.class,1);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("Text parsed",txt.getData().toString(),is(message)); Assert.assertThat("Text parsed",txt.getPayloadAsText(),is(message));
} }
@Test @Test
@ -82,6 +82,6 @@ public class GeneratorParserRoundtripTest
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertTrue("Text.isMasked",txt.isMasked()); Assert.assertTrue("Text.isMasked",txt.isMasked());
Assert.assertThat("Text parsed",txt.getData().toString(),is(message)); Assert.assertThat("Text parsed",txt.getPayloadAsText(),is(message));
} }
} }

View File

@ -40,8 +40,9 @@ public class RFC6455ExamplesGeneratorTest
t1.setFin(false); t1.setFin(false);
t2.setFin(true); t2.setFin(true);
t1.setData("Hel"); t2.setContinuation(true);
t2.setData("lo"); t1.setPayload("Hel");
t2.setPayload("lo");
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
@ -57,12 +58,7 @@ public class RFC6455ExamplesGeneratorTest
g1.flip(); g1.flip();
b2.flip(); b2.flip();
g2.flip(); g2.flip();
Assert.assertEquals(b1.get(),g1.get());
System.out.println(Integer.toHexString(b2.get()));
System.out.println(Integer.toHexString(g2.get()));
ByteBufferAssert.assertEquals("t1 buffers are not equal", b1, g1); ByteBufferAssert.assertEquals("t1 buffers are not equal", b1, g1);
ByteBufferAssert.assertEquals("t2 buffers are not equal", b2, g2); ByteBufferAssert.assertEquals("t2 buffers are not equal", b2, g2);
@ -149,7 +145,7 @@ public class RFC6455ExamplesGeneratorTest
{ (byte)0x81, (byte)0x85, 0x37, (byte)0xfa, 0x21, 0x3d, 0x7f, (byte)0x9f, 0x4d, 0x51, 0x58 }); { (byte)0x81, (byte)0x85, 0x37, (byte)0xfa, 0x21, 0x3d, 0x7f, (byte)0x9f, 0x4d, 0x51, 0x58 });
TextFrame t1 = new TextFrame(); TextFrame t1 = new TextFrame();
t1.setData("Hello"); t1.setPayload("Hello");
t1.setFin(true); t1.setFin(true);
t1.setMasked(true); t1.setMasked(true);
t1.setMask(new byte[]{(byte)0xff,(byte)0xff,(byte)0xff,(byte)0xff}); t1.setMask(new byte[]{(byte)0xff,(byte)0xff,(byte)0xff,(byte)0xff});

View File

@ -51,9 +51,9 @@ public class RFC6455ExamplesParserTest
capture.assertHasFrame(TextFrame.class,2); capture.assertHasFrame(TextFrame.class,2);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("TextFrame[0].data",txt.getData().toString(),is("Hel")); Assert.assertThat("TextFrame[0].data",txt.getPayloadAsText(),is("Hel"));
txt = (TextFrame)capture.getFrames().get(1); txt = (TextFrame)capture.getFrames().get(1);
Assert.assertThat("TextFrame[1].data",txt.getData().toString(),is("lo")); Assert.assertThat("TextFrame[1].data",txt.getPayloadAsText(),is("lo"));
} }
@Test @Test
@ -99,7 +99,7 @@ public class RFC6455ExamplesParserTest
capture.assertHasFrame(TextFrame.class,1); capture.assertHasFrame(TextFrame.class,1);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("TextFrame.data",txt.getData().toString(),is("Hello")); Assert.assertThat("TextFrame.data",txt.getPayloadAsText(),is("Hello"));
} }
@Test @Test
@ -129,12 +129,12 @@ public class RFC6455ExamplesParserTest
capture.assertHasFrame(BinaryFrame.class,1); capture.assertHasFrame(BinaryFrame.class,1);
BinaryFrame bin = (BinaryFrame)capture.getFrames().get(0); BinaryFrame bin = (BinaryFrame)capture.getFrames().get(0);
bin.getData().flip(); bin.getPayload().flip();
Assert.assertThat("BinaryFrame.payloadLength",bin.getPayloadLength(),is(dataSize)); Assert.assertThat("BinaryFrame.payloadLength",bin.getPayloadLength(),is(dataSize));
ByteBufferAssert.assertSize("BinaryFrame.payload",dataSize,bin.getData()); ByteBufferAssert.assertSize("BinaryFrame.payload",dataSize,bin.getPayload());
ByteBuffer data = bin.getData(); ByteBuffer data = bin.getPayload();
for (int i = dataSize; i > 0; i--) for (int i = dataSize; i > 0; i--)
{ {
Assert.assertThat("BinaryFrame.data[" + i + "]",data.get(),is((byte)0x44)); Assert.assertThat("BinaryFrame.data[" + i + "]",data.get(),is((byte)0x44));
@ -168,12 +168,12 @@ public class RFC6455ExamplesParserTest
capture.assertHasFrame(BinaryFrame.class,1); capture.assertHasFrame(BinaryFrame.class,1);
BinaryFrame bin = (BinaryFrame)capture.getFrames().get(0); BinaryFrame bin = (BinaryFrame)capture.getFrames().get(0);
bin.getData().flip(); bin.getPayload().flip();
Assert.assertThat("BinaryFrame.payloadLength",bin.getPayloadLength(),is(dataSize)); Assert.assertThat("BinaryFrame.payloadLength",bin.getPayloadLength(),is(dataSize));
ByteBufferAssert.assertSize("BinaryFrame.payload",dataSize,bin.getData()); ByteBufferAssert.assertSize("BinaryFrame.payload",dataSize,bin.getPayload());
ByteBuffer data = bin.getData(); ByteBuffer data = bin.getPayload();
for (int i = dataSize; i > 0; i--) for (int i = dataSize; i > 0; i--)
{ {
Assert.assertThat("BinaryFrame.data[" + i + "]",data.get(),is((byte)0x77)); Assert.assertThat("BinaryFrame.data[" + i + "]",data.get(),is((byte)0x77));
@ -223,6 +223,6 @@ public class RFC6455ExamplesParserTest
capture.assertHasFrame(TextFrame.class,1); capture.assertHasFrame(TextFrame.class,1);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("TextFrame.data", txt.getData().toString(), is("Hello")); Assert.assertThat("TextFrame.data", txt.getPayloadAsText(), is("Hello"));
} }
} }

View File

@ -78,7 +78,7 @@ public class TextPayloadParserTest
capture.assertNoErrors(); capture.assertNoErrors();
capture.assertHasFrame(TextFrame.class,1); capture.assertHasFrame(TextFrame.class,1);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("TextFrame.data",txt.getData().toString(),is(expectedText)); Assert.assertThat("TextFrame.data",txt.getPayloadAsText(),is(expectedText));
} }
@Test @Test
@ -113,7 +113,7 @@ public class TextPayloadParserTest
capture.assertNoErrors(); capture.assertNoErrors();
capture.assertHasFrame(TextFrame.class,1); capture.assertHasFrame(TextFrame.class,1);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("TextFrame.data",txt.getData().toString(),is(expectedText)); Assert.assertThat("TextFrame.data",txt.getPayloadAsText(),is(expectedText));
} }
@Test @Test
@ -150,9 +150,9 @@ public class TextPayloadParserTest
capture.assertNoErrors(); capture.assertNoErrors();
capture.assertHasFrame(TextFrame.class,2); capture.assertHasFrame(TextFrame.class,2);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("TextFrame[0].data",txt.getData().toString(),is(part1)); Assert.assertThat("TextFrame[0].data",txt.getPayloadAsText(),is(part1));
txt = (TextFrame)capture.getFrames().get(1); txt = (TextFrame)capture.getFrames().get(1);
Assert.assertThat("TextFrame[1].data",txt.getData().toString(),is(part2)); Assert.assertThat("TextFrame[1].data",txt.getPayloadAsText(),is(part2));
} }
@Test @Test
@ -177,7 +177,7 @@ public class TextPayloadParserTest
capture.assertNoErrors(); capture.assertNoErrors();
capture.assertHasFrame(TextFrame.class,1); capture.assertHasFrame(TextFrame.class,1);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("TextFrame.data",txt.getData().toString(),is(expectedText)); Assert.assertThat("TextFrame.data",txt.getPayloadAsText(),is(expectedText));
} }
@Test @Test
@ -203,6 +203,6 @@ public class TextPayloadParserTest
capture.assertNoErrors(); capture.assertNoErrors();
capture.assertHasFrame(TextFrame.class,1); capture.assertHasFrame(TextFrame.class,1);
TextFrame txt = (TextFrame)capture.getFrames().get(0); TextFrame txt = (TextFrame)capture.getFrames().get(0);
Assert.assertThat("TextFrame.data",txt.getData().toString(),is(expectedText)); Assert.assertThat("TextFrame.data",txt.getPayloadAsText(),is(expectedText));
} }
} }