use of org.eclipse.jetty.websocket.common.frames.ContinuationFrame in project jetty.project by eclipse.
the class Parser method parseFrame.
/**
* Parse the base framing protocol buffer.
* <p>
* Note the first byte (fin,rsv1,rsv2,rsv3,opcode) are parsed by the {@link Parser#parse(ByteBuffer)} method
* <p>
* Not overridable
*
* @param buffer
* the buffer to parse from.
* @return true if done parsing base framing protocol and ready for parsing of the payload. false if incomplete parsing of base framing protocol.
*/
private boolean parseFrame(ByteBuffer buffer) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} Parsing {} bytes", policy.getBehavior(), buffer.remaining());
}
while (buffer.hasRemaining()) {
switch(state) {
case START:
{
// peek at byte
byte b = buffer.get();
boolean fin = ((b & 0x80) != 0);
byte opcode = (byte) (b & 0x0F);
if (!OpCode.isKnown(opcode)) {
throw new ProtocolException("Unknown opcode: " + opcode);
}
if (LOG.isDebugEnabled())
LOG.debug("{} OpCode {}, fin={} rsv={}{}{}", policy.getBehavior(), OpCode.name(opcode), fin, (((b & 0x40) != 0) ? '1' : '.'), (((b & 0x20) != 0) ? '1' : '.'), (((b & 0x10) != 0) ? '1' : '.'));
// base framing flags
switch(opcode) {
case OpCode.TEXT:
frame = new TextFrame();
// data validation
if (priorDataFrame) {
throw new ProtocolException("Unexpected " + OpCode.name(opcode) + " frame, was expecting CONTINUATION");
}
break;
case OpCode.BINARY:
frame = new BinaryFrame();
// data validation
if (priorDataFrame) {
throw new ProtocolException("Unexpected " + OpCode.name(opcode) + " frame, was expecting CONTINUATION");
}
break;
case OpCode.CONTINUATION:
frame = new ContinuationFrame();
// continuation validation
if (!priorDataFrame) {
throw new ProtocolException("CONTINUATION frame without prior !FIN");
}
// Be careful to use the original opcode
break;
case OpCode.CLOSE:
frame = new CloseFrame();
// control frame validation
if (!fin) {
throw new ProtocolException("Fragmented Close Frame [" + OpCode.name(opcode) + "]");
}
break;
case OpCode.PING:
frame = new PingFrame();
// control frame validation
if (!fin) {
throw new ProtocolException("Fragmented Ping Frame [" + OpCode.name(opcode) + "]");
}
break;
case OpCode.PONG:
frame = new PongFrame();
// control frame validation
if (!fin) {
throw new ProtocolException("Fragmented Pong Frame [" + OpCode.name(opcode) + "]");
}
break;
}
frame.setFin(fin);
// Are any flags set?
if ((b & 0x70) != 0) {
/*
* 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 ((b & 0x40) != 0) {
if (isRsv1InUse())
frame.setRsv1(true);
else {
String err = "RSV1 not allowed to be set";
if (LOG.isDebugEnabled()) {
LOG.debug(err + ": Remaining buffer: {}", BufferUtil.toDetailString(buffer));
}
throw new ProtocolException(err);
}
}
if ((b & 0x20) != 0) {
if (isRsv2InUse())
frame.setRsv2(true);
else {
String err = "RSV2 not allowed to be set";
if (LOG.isDebugEnabled()) {
LOG.debug(err + ": Remaining buffer: {}", BufferUtil.toDetailString(buffer));
}
throw new ProtocolException(err);
}
}
if ((b & 0x10) != 0) {
if (isRsv3InUse())
frame.setRsv3(true);
else {
String err = "RSV3 not allowed to be set";
if (LOG.isDebugEnabled()) {
LOG.debug(err + ": Remaining buffer: {}", BufferUtil.toDetailString(buffer));
}
throw new ProtocolException(err);
}
}
}
state = State.PAYLOAD_LEN;
break;
}
case PAYLOAD_LEN:
{
byte b = buffer.get();
frame.setMasked((b & 0x80) != 0);
payloadLength = (byte) (0x7F & b);
if (// 0x7F
payloadLength == 127) {
// length 8 bytes (extended payload length)
payloadLength = 0;
state = State.PAYLOAD_LEN_BYTES;
cursor = 8;
// continue onto next state
break;
} else if (// 0x7E
payloadLength == 126) {
// length 2 bytes (extended payload length)
payloadLength = 0;
state = State.PAYLOAD_LEN_BYTES;
cursor = 2;
// continue onto next state
break;
}
assertSanePayloadLength(payloadLength);
if (frame.isMasked()) {
state = State.MASK;
} else {
// special case for empty payloads (no more bytes left in buffer)
if (payloadLength == 0) {
state = State.START;
return true;
}
maskProcessor.reset(frame);
state = State.PAYLOAD;
}
break;
}
case PAYLOAD_LEN_BYTES:
{
byte b = buffer.get();
--cursor;
payloadLength |= (b & 0xFF) << (8 * cursor);
if (cursor == 0) {
assertSanePayloadLength(payloadLength);
if (frame.isMasked()) {
state = State.MASK;
} else {
// special case for empty payloads (no more bytes left in buffer)
if (payloadLength == 0) {
state = State.START;
return true;
}
maskProcessor.reset(frame);
state = State.PAYLOAD;
}
}
break;
}
case MASK:
{
byte[] m = new byte[4];
frame.setMask(m);
if (buffer.remaining() >= 4) {
buffer.get(m, 0, 4);
// special case for empty payloads (no more bytes left in buffer)
if (payloadLength == 0) {
state = State.START;
return true;
}
maskProcessor.reset(frame);
state = State.PAYLOAD;
} else {
state = State.MASK_BYTES;
cursor = 4;
}
break;
}
case MASK_BYTES:
{
byte b = buffer.get();
frame.getMask()[4 - cursor] = b;
--cursor;
if (cursor == 0) {
// special case for empty payloads (no more bytes left in buffer)
if (payloadLength == 0) {
state = State.START;
return true;
}
maskProcessor.reset(frame);
state = State.PAYLOAD;
}
break;
}
case PAYLOAD:
{
frame.assertValid();
if (parsePayload(buffer)) {
// special check for close
if (frame.getOpCode() == OpCode.CLOSE) {
// TODO: yuck. Don't create an object to do validation checks!
new CloseInfo(frame);
}
state = State.START;
// we have a frame!
return true;
}
break;
}
}
}
return false;
}
use of org.eclipse.jetty.websocket.common.frames.ContinuationFrame in project jetty.project by eclipse.
the class WebSocketRemoteEndpoint method sendPartialString.
@Override
public void sendPartialString(String fragment, boolean isLast) throws IOException {
boolean first = lockMsg(MsgType.PARTIAL_TEXT);
try {
if (LOG.isDebugEnabled()) {
LOG.debug("sendPartialString({}, {})", fragment, isLast);
}
DataFrame frame = first ? new TextFrame() : new ContinuationFrame();
frame.setPayload(BufferUtil.toBuffer(fragment, StandardCharsets.UTF_8));
frame.setFin(isLast);
blockingWrite(frame);
} finally {
if (isLast)
unlockMsg(MsgType.PARTIAL_TEXT);
}
}
use of org.eclipse.jetty.websocket.common.frames.ContinuationFrame in project jetty.project by eclipse.
the class ParserTest method testParseCase5_15.
/**
* Similar to the server side 5.15 testcase. A normal 2 fragment text text message, followed by another continuation.
*/
@Test
public void testParseCase5_15() {
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setPayload("fragment1").setFin(false));
send.add(new ContinuationFrame().setPayload("fragment2").setFin(true));
// bad frame
send.add(new ContinuationFrame().setPayload("fragment3").setFin(false));
send.add(new TextFrame().setPayload("fragment4").setFin(true));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
ByteBuffer completeBuf = UnitGenerator.generate(send);
UnitParser parser = new UnitParser();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parseQuietly(completeBuf);
capture.assertErrorCount(1);
capture.assertHasFrame(OpCode.TEXT, 1);
capture.assertHasFrame(OpCode.CONTINUATION, 1);
}
use of org.eclipse.jetty.websocket.common.frames.ContinuationFrame in project jetty.project by eclipse.
the class ParserTest method testParseCase6_2_3.
/**
* Similar to the server side 6.2.3 testcase. Lots of small 1 byte UTF8 Text frames, representing 1 overall text message.
*/
@Test
public void testParseCase6_2_3() {
String utf8 = "Hello-습@쎟쎤쎼쎠쎡-UTF-8!!";
byte[] msg = StringUtil.getUtf8Bytes(utf8);
List<WebSocketFrame> send = new ArrayList<>();
int textCount = 0;
int continuationCount = 0;
int len = msg.length;
boolean continuation = false;
byte[] mini;
for (int i = 0; i < len; i++) {
DataFrame frame = null;
if (continuation) {
frame = new ContinuationFrame();
continuationCount++;
} else {
frame = new TextFrame();
textCount++;
}
mini = new byte[1];
mini[0] = msg[i];
frame.setPayload(ByteBuffer.wrap(mini));
boolean isLast = (i >= (len - 1));
frame.setFin(isLast);
send.add(frame);
continuation = true;
}
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
ByteBuffer completeBuf = UnitGenerator.generate(send);
UnitParser parser = new UnitParser();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(completeBuf);
capture.assertErrorCount(0);
capture.assertHasFrame(OpCode.TEXT, textCount);
capture.assertHasFrame(OpCode.CONTINUATION, continuationCount);
capture.assertHasFrame(OpCode.CLOSE, 1);
}
use of org.eclipse.jetty.websocket.common.frames.ContinuationFrame in project jetty.project by eclipse.
the class OnPartialTest method testOnTextPartial.
@Test
public void testOnTextPartial() throws Throwable {
List<WebSocketFrame> frames = new ArrayList<>();
frames.add(new TextFrame().setPayload("Saved").setFin(false));
frames.add(new ContinuationFrame().setPayload(" by ").setFin(false));
frames.add(new ContinuationFrame().setPayload("zero").setFin(true));
PartialTrackingSocket socket = new PartialTrackingSocket();
EventDriver driver = toEventDriver(socket);
driver.onConnect();
for (WebSocketFrame frame : frames) {
driver.incomingFrame(frame);
}
Assert.assertThat("Captured Event Queue size", socket.eventQueue.size(), is(3));
Assert.assertThat("Event[0]", socket.eventQueue.poll(), is("onPartial(\"Saved\",false)"));
Assert.assertThat("Event[1]", socket.eventQueue.poll(), is("onPartial(\" by \",false)"));
Assert.assertThat("Event[2]", socket.eventQueue.poll(), is("onPartial(\"zero\",true)"));
}
Aggregations