rejigger frame builder to use the generator and pull generator validation logic into assertFrameValid method

This commit is contained in:
Jesse McConnell 2012-07-06 17:25:58 -05:00
parent 27b05ccd6e
commit 889a471420
3 changed files with 67 additions and 148 deletions

View File

@ -49,17 +49,54 @@ public class FrameGenerator
this.policy = policy;
}
public void assertFrameValid(WebSocketFrame frame)
{
if (frame.getOpCode().isControlFrame() && !frame.isFin())
{
throw new ProtocolException("Control Frames must be FIN=true");
}
if (frame.isRsv1())
{
// TODO: extensions can negotiate this (somehow)
throw new ProtocolException("RSV1 not allowed to be set");
}
if (frame.isRsv2())
{
// TODO: extensions can negotiate this (somehow)
throw new ProtocolException("RSV2 not allowed to be set");
}
if (frame.isRsv3())
{
// TODO: extensions can negotiate this (somehow)
throw new ProtocolException("RSV3 not allowed to be set");
}
if (frame.getOpCode().isControlFrame())
{
if (frame.getPayloadLength() > 125)
{
throw new ProtocolException("Invalid control frame payload length");
}
if (frame.getOpCode() == OpCode.CLOSE)
{
new CloseInfo(frame.getPayloadData(),true);
}
}
}
public ByteBuffer generate(ByteBuffer buffer, WebSocketFrame frame)
{
LOG.debug(String.format("Generate.Frame[opcode=%s,fin=%b,cont=%b,rsv1=%b,rsv2=%b,rsv3=%b,mask=%b,plength=%d]",frame.getOpCode().toString(),
frame.isFin(),frame.isContinuation(),frame.isRsv1(),frame.isRsv2(),frame.isRsv3(),frame.isMasked(),frame.getPayloadLength()));
byte b;
assertFrameValid(frame);
if ( frame.getOpCode().isControlFrame() && !frame.isFin() )
{
throw new ProtocolException("Control Frames must be FIN=true");
}
byte b;
// Setup fin thru opcode
b = 0x00;
@ -70,20 +107,14 @@ public class FrameGenerator
if (frame.isRsv1())
{
b |= 0x40; // 0100_0000
// TODO: extensions can negotiate this (somehow)
throw new ProtocolException("RSV1 not allowed to be set");
}
if (frame.isRsv2())
{
b |= 0x20; // 0010_0000
// TODO: extensions can negotiate this (somehow)
throw new ProtocolException("RSV2 not allowed to be set");
}
if (frame.isRsv3())
{
b |= 0x10;
// TODO: extensions can negotiate this (somehow)
throw new ProtocolException("RSV3 not allowed to be set");
}
byte opcode = frame.getOpCode().getCode();
@ -145,31 +176,12 @@ public class FrameGenerator
// masking key
if (frame.isMasked())
{
// TODO: figure out maskgen
buffer.put(frame.getMask());
}
// now the payload itself
// call back into masking check/method on this class?
// remember the position
int positionPrePayload = buffer.position();
if (frame.getOpCode().isControlFrame())
{
if (frame.getPayloadLength() > 125)
{
throw new ProtocolException("Invalid control frame payload length");
}
}
if (frame.getOpCode() == OpCode.CLOSE)
{
// validate the close
new CloseInfo(frame.getPayloadData(),true);
}
// copy payload
if (frame.hasPayload())
{

View File

@ -4,17 +4,36 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.api.PolicyViolationException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.generator.FrameGenerator;
/**
* The FrameBuilder applies a builder pattern to constructing WebSocketFrame classes.
*
* WARNING: It is possible to build bad frames using this builder which is the intent
* WARNING: It is possible to build bad frames using this builder which is intended
*
*/
public class FrameBuilder
{
public class DirtyGenerator extends FrameGenerator
{
public DirtyGenerator()
{
super(WebSocketPolicy.newServerPolicy());
}
@Override
public void assertFrameValid(WebSocketFrame frame)
{
/*
* we desire the ability to craft bad frames so ignore frame validation
*/
}
}
public static FrameBuilder binary()
{
return new FrameBuilder(new WebSocketFrame(OpCode.BINARY));
@ -102,6 +121,8 @@ public class FrameBuilder
return new FrameBuilder(new WebSocketFrame(OpCode.TEXT)).payload(text.getBytes(StringUtil.__UTF8_CHARSET));
}
DirtyGenerator generator = new DirtyGenerator();
private WebSocketFrame frame;
public FrameBuilder(WebSocketFrame frame)
@ -129,122 +150,7 @@ public class FrameBuilder
public void fill(ByteBuffer buffer)
{
byte b;
// Setup fin thru opcode
b = 0x00;
if (frame.isFin())
{
b |= 0x80; // 1000_0000
}
if (frame.isRsv1())
{
b |= 0x40; // 0100_0000
// TODO: extensions can negotiate this (somehow)
throw new PolicyViolationException("RSV1 not allowed to be set");
}
if (frame.isRsv2())
{
b |= 0x20; // 0010_0000
// TODO: extensions can negotiate this (somehow)
throw new PolicyViolationException("RSV2 not allowed to be set");
}
if (frame.isRsv3())
{
b |= 0x10;
// TODO: extensions can negotiate this (somehow)
throw new PolicyViolationException("RSV3 not allowed to be set");
}
byte opcode = frame.getOpCode().getCode();
if (frame.isContinuation())
{
// Continuations are not the same OPCODE
opcode = OpCode.CONTINUATION.getCode();
}
b |= opcode & 0x0F;
buffer.put(b);
// is masked
b = 0x00;
b |= (frame.isMasked()?0x80:0x00);
// payload lengths
int payloadLength = frame.getPayloadLength();
/*
* if length is over 65535 then its a 7 + 64 bit length
*/
if (payloadLength > 0xFF_FF)
{
// we have a 64 bit length
b |= 0x7F;
buffer.put(b); // indicate 8 byte length
buffer.put((byte)0); //
buffer.put((byte)0); // anything over an
buffer.put((byte)0); // int is just
buffer.put((byte)0); // intsane!
buffer.put((byte)((payloadLength >> 24) & 0xFF));
buffer.put((byte)((payloadLength >> 16) & 0xFF));
buffer.put((byte)((payloadLength >> 8) & 0xFF));
buffer.put((byte)(payloadLength & 0xFF));
}
/*
* if payload is ge 126 we have a 7 + 16 bit length
*/
else if (payloadLength >= 0x7E)
{
b |= 0x7E;
buffer.put(b); // indicate 2 byte length
buffer.put((byte)(payloadLength >> 8));
buffer.put((byte)(payloadLength & 0xFF));
}
/*
* we have a 7 bit length
*/
else
{
b |= (payloadLength & 0x7F);
buffer.put(b);
}
// masking key
if (frame.isMasked())
{
// TODO: figure out maskgen
buffer.put(frame.getMask());
}
// now the payload itself
// remember the position
int positionPrePayload = buffer.position();
// generate payload
if (frame.getPayloadLength() > 0)
{
buffer.put(frame.getPayloadData());
}
int positionPostPayload = buffer.position();
// mask it if needed
if (frame.isMasked())
{
// move back to remembered position.
int size = positionPostPayload - positionPrePayload;
byte[] mask = frame.getMask();
int pos;
for (int i = 0; i < size; i++)
{
pos = positionPrePayload + i;
// Mask each byte by its absolute position in the bytebuffer
buffer.put(pos,(byte)(buffer.get(pos) ^ mask[i % 4]));
}
}
generator.generate(buffer,frame);
BufferUtil.flipToFlush(buffer,0);
}

View File

@ -48,4 +48,5 @@ public class FrameBuilderTest
Assert.assertArrayEquals(expected,actual);
}
}