Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9
This commit is contained in:
commit
057acfd665
|
@ -1,231 +0,0 @@
|
|||
package org.eclipse.jetty.websocket.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.ProtocolException;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.protocol.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.protocol.OpCode;
|
||||
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
* |F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||||
* |I|S|S|S| (4) |A| (7) | (16/64) |
|
||||
* |N|V|V|V| |S| | (if payload len==126/127) |
|
||||
* | |1|2|3| |K| | |
|
||||
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||||
* | Extended payload length continued, if payload len == 127 |
|
||||
* + - - - - - - - - - - - - - - - +-------------------------------+
|
||||
* | |Masking-key, if MASK set to 1 |
|
||||
* +-------------------------------+-------------------------------+
|
||||
* | Masking-key (continued) | Payload Data |
|
||||
* +-------------------------------- - - - - - - - - - - - - - - - +
|
||||
* : Payload Data continued ... :
|
||||
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
||||
* | Payload Data continued ... |
|
||||
* +---------------------------------------------------------------+
|
||||
* </pre>
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class FrameGenerator
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(FrameGenerator.class);
|
||||
|
||||
/**
|
||||
* The overhead (maximum) for a framing header. Assuming a maximum sized payload with masking key.
|
||||
*/
|
||||
public static final int OVERHEAD = 28;
|
||||
private final WebSocketPolicy policy;
|
||||
|
||||
public FrameGenerator(WebSocketPolicy policy)
|
||||
{
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
public void assertFrameValid(WebSocketFrame frame)
|
||||
{
|
||||
/*
|
||||
* RFC 6455 Section 5.2
|
||||
*
|
||||
* MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated
|
||||
* extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
|
||||
*/
|
||||
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())
|
||||
{
|
||||
/*
|
||||
* RFC 6455 Section 5.5
|
||||
*
|
||||
* All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.
|
||||
*/
|
||||
if (frame.getPayloadLength() > 125)
|
||||
{
|
||||
throw new ProtocolException("Invalid control frame payload length");
|
||||
}
|
||||
|
||||
if (!frame.isFin())
|
||||
{
|
||||
throw new ProtocolException("Control Frames must be FIN=true");
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 6455 Section 5.5.1
|
||||
*
|
||||
* close frame payload is specially formatted which is checked in CloseInfo
|
||||
*/
|
||||
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()));
|
||||
|
||||
assertFrameValid(frame);
|
||||
|
||||
byte b;
|
||||
|
||||
// Setup fin thru opcode
|
||||
b = 0x00;
|
||||
if (frame.isFin())
|
||||
{
|
||||
b |= 0x80; // 1000_0000
|
||||
}
|
||||
if (frame.isRsv1())
|
||||
{
|
||||
b |= 0x40; // 0100_0000
|
||||
}
|
||||
if (frame.isRsv2())
|
||||
{
|
||||
b |= 0x20; // 0010_0000
|
||||
}
|
||||
if (frame.isRsv3())
|
||||
{
|
||||
b |= 0x10;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
buffer.put(frame.getMask());
|
||||
}
|
||||
|
||||
// remember the position
|
||||
int positionPrePayload = buffer.position();
|
||||
|
||||
// copy payload
|
||||
if (frame.hasPayload())
|
||||
{
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public WebSocketPolicy getPolicy()
|
||||
{
|
||||
return policy;
|
||||
}
|
||||
}
|
|
@ -6,7 +6,10 @@ import org.eclipse.jetty.io.ByteBufferPool;
|
|||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.ProtocolException;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.protocol.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.protocol.OpCode;
|
||||
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
|
||||
|
||||
/**
|
||||
|
@ -36,47 +39,209 @@ import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
|
|||
public class Generator
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(Generator.class);
|
||||
/**
|
||||
* The overhead (maximum) for a framing header. Assuming a maximum sized payload with masking key.
|
||||
*/
|
||||
public static final int OVERHEAD = 28;
|
||||
|
||||
private final FrameGenerator basicGenerator;
|
||||
private final WebSocketPolicy policy;
|
||||
private final ByteBufferPool bufferPool;
|
||||
|
||||
public Generator(WebSocketPolicy policy, ByteBufferPool bufferPool)
|
||||
{
|
||||
basicGenerator = new FrameGenerator(policy,bufferPool);
|
||||
this.policy = policy;
|
||||
this.bufferPool = bufferPool;
|
||||
}
|
||||
|
||||
public ByteBuffer generate(ByteBuffer buffer, WebSocketFrame frame)
|
||||
public void assertFrameValid(WebSocketFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
/*
|
||||
* RFC 6455 Section 5.2
|
||||
*
|
||||
* MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated
|
||||
* extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
|
||||
*/
|
||||
if (frame.isRsv1())
|
||||
{
|
||||
LOG.debug("To Generate: {}",frame);
|
||||
// TODO: extensions can negotiate this (somehow)
|
||||
throw new ProtocolException("RSV1 not allowed to be set");
|
||||
}
|
||||
ByteBuffer ret = basicGenerator.generate(buffer,frame);
|
||||
if (LOG.isDebugEnabled())
|
||||
|
||||
if (frame.isRsv2())
|
||||
{
|
||||
LOG.debug("Generated[{}]: {}",basicGenerator.getClass().getSimpleName(),BufferUtil.toDetailString(buffer));
|
||||
// TODO: extensions can negotiate this (somehow)
|
||||
throw new ProtocolException("RSV2 not allowed to be set");
|
||||
}
|
||||
return ret;
|
||||
|
||||
if (frame.isRsv3())
|
||||
{
|
||||
// TODO: extensions can negotiate this (somehow)
|
||||
throw new ProtocolException("RSV3 not allowed to be set");
|
||||
}
|
||||
|
||||
if (frame.getOpCode().isControlFrame())
|
||||
{
|
||||
/*
|
||||
* RFC 6455 Section 5.5
|
||||
*
|
||||
* All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.
|
||||
*/
|
||||
if (frame.getPayloadLength() > 125)
|
||||
{
|
||||
throw new ProtocolException("Invalid control frame payload length");
|
||||
}
|
||||
|
||||
if (!frame.isFin())
|
||||
{
|
||||
throw new ProtocolException("Control Frames must be FIN=true");
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 6455 Section 5.5.1
|
||||
*
|
||||
* close frame payload is specially formatted which is checked in CloseInfo
|
||||
*/
|
||||
if (frame.getOpCode() == OpCode.CLOSE)
|
||||
{
|
||||
new CloseInfo(frame.getPayloadData(),true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ByteBuffer generate(int size, WebSocketFrame frame)
|
||||
{
|
||||
// NEW ONE - allocate and fill only to param size
|
||||
// note: size is the buffer size to generate into, not related to the payload size
|
||||
// note: size cannot be less than framing overhead
|
||||
// TODO: update frame.setAvailable() to indicate how much of payload is left to be generated
|
||||
return basicGenerator.generate(frame);
|
||||
}
|
||||
|
||||
public ByteBuffer generate(WebSocketFrame frame)
|
||||
{
|
||||
// NEW ONE - allocate as much as needed
|
||||
return basicGenerator.generate(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()));
|
||||
|
||||
assertFrameValid(frame);
|
||||
|
||||
/*
|
||||
* prepare the byte buffer to put frame into
|
||||
*/
|
||||
ByteBuffer buffer = bufferPool.acquire(frame.getPayloadLength() + OVERHEAD,true);
|
||||
BufferUtil.clearToFill(buffer);
|
||||
|
||||
/*
|
||||
* start the generation process
|
||||
*/
|
||||
byte b;
|
||||
|
||||
// Setup fin thru opcode
|
||||
b = 0x00;
|
||||
if (frame.isFin())
|
||||
{
|
||||
b |= 0x80; // 1000_0000
|
||||
}
|
||||
if (frame.isRsv1())
|
||||
{
|
||||
b |= 0x40; // 0100_0000
|
||||
}
|
||||
if (frame.isRsv2())
|
||||
{
|
||||
b |= 0x20; // 0010_0000
|
||||
}
|
||||
if (frame.isRsv3())
|
||||
{
|
||||
b |= 0x10;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
buffer.put(frame.getMask());
|
||||
}
|
||||
|
||||
// remember the position
|
||||
int positionPrePayload = buffer.position();
|
||||
|
||||
// copy payload
|
||||
if (frame.hasPayload())
|
||||
{
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("Generator [basic=%s]",basicGenerator.getClass().getSimpleName());
|
||||
return String.format("Generator [basic=%s]",this.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ package org.eclipse.jetty.websocket.protocol;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.StandardByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.generator.FrameGenerator;
|
||||
import org.eclipse.jetty.websocket.generator.Generator;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -16,12 +18,12 @@ import org.eclipse.jetty.websocket.generator.FrameGenerator;
|
|||
*/
|
||||
public class FrameBuilder
|
||||
{
|
||||
public class DirtyGenerator extends FrameGenerator
|
||||
public class DirtyGenerator extends Generator
|
||||
{
|
||||
|
||||
public DirtyGenerator()
|
||||
{
|
||||
super(WebSocketPolicy.newServerPolicy());
|
||||
super(WebSocketPolicy.newServerPolicy(),bufferPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,6 +36,8 @@ public class FrameBuilder
|
|||
|
||||
}
|
||||
|
||||
private static ByteBufferPool bufferPool = new StandardByteBufferPool();
|
||||
|
||||
public static FrameBuilder binary()
|
||||
{
|
||||
return new FrameBuilder(new WebSocketFrame(OpCode.BINARY));
|
||||
|
@ -138,9 +142,7 @@ public class FrameBuilder
|
|||
|
||||
public ByteBuffer asByteBuffer()
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.allocate(frame.getPayloadLength() + 32 );
|
||||
fill(buffer);
|
||||
return buffer;
|
||||
return generator.generate(frame);
|
||||
}
|
||||
|
||||
public WebSocketFrame asFrame()
|
||||
|
@ -148,13 +150,6 @@ public class FrameBuilder
|
|||
return frame;
|
||||
}
|
||||
|
||||
public void fill(ByteBuffer buffer)
|
||||
{
|
||||
generator.generate(buffer,frame);
|
||||
|
||||
BufferUtil.flipToFlush(buffer,0);
|
||||
}
|
||||
|
||||
public FrameBuilder fin( boolean fin )
|
||||
{
|
||||
frame.setFin(fin);
|
||||
|
|
Loading…
Reference in New Issue