Search in sources :

Example 1 with ContinuationFrame

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;
}
Also used : PongFrame(org.eclipse.jetty.websocket.common.frames.PongFrame) ProtocolException(org.eclipse.jetty.websocket.api.ProtocolException) BinaryFrame(org.eclipse.jetty.websocket.common.frames.BinaryFrame) PingFrame(org.eclipse.jetty.websocket.common.frames.PingFrame) TextFrame(org.eclipse.jetty.websocket.common.frames.TextFrame) CloseFrame(org.eclipse.jetty.websocket.common.frames.CloseFrame) ContinuationFrame(org.eclipse.jetty.websocket.common.frames.ContinuationFrame)

Example 2 with ContinuationFrame

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);
    }
}
Also used : TextFrame(org.eclipse.jetty.websocket.common.frames.TextFrame) DataFrame(org.eclipse.jetty.websocket.common.frames.DataFrame) ContinuationFrame(org.eclipse.jetty.websocket.common.frames.ContinuationFrame)

Example 3 with ContinuationFrame

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);
}
Also used : ArrayList(java.util.ArrayList) TextFrame(org.eclipse.jetty.websocket.common.frames.TextFrame) IncomingFramesCapture(org.eclipse.jetty.websocket.common.test.IncomingFramesCapture) ContinuationFrame(org.eclipse.jetty.websocket.common.frames.ContinuationFrame) UnitParser(org.eclipse.jetty.websocket.common.test.UnitParser) ByteBuffer(java.nio.ByteBuffer) Test(org.junit.Test)

Example 4 with ContinuationFrame

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);
}
Also used : ArrayList(java.util.ArrayList) DataFrame(org.eclipse.jetty.websocket.common.frames.DataFrame) ContinuationFrame(org.eclipse.jetty.websocket.common.frames.ContinuationFrame) UnitParser(org.eclipse.jetty.websocket.common.test.UnitParser) ByteBuffer(java.nio.ByteBuffer) TextFrame(org.eclipse.jetty.websocket.common.frames.TextFrame) IncomingFramesCapture(org.eclipse.jetty.websocket.common.test.IncomingFramesCapture) Test(org.junit.Test)

Example 5 with ContinuationFrame

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)"));
}
Also used : EventDriver(org.eclipse.jetty.websocket.common.events.EventDriver) PartialTrackingSocket(org.eclipse.jetty.websocket.jsr356.server.samples.partial.PartialTrackingSocket) ArrayList(java.util.ArrayList) TextFrame(org.eclipse.jetty.websocket.common.frames.TextFrame) WebSocketFrame(org.eclipse.jetty.websocket.common.WebSocketFrame) ContinuationFrame(org.eclipse.jetty.websocket.common.frames.ContinuationFrame) Test(org.junit.Test)

Aggregations

ContinuationFrame (org.eclipse.jetty.websocket.common.frames.ContinuationFrame)39 TextFrame (org.eclipse.jetty.websocket.common.frames.TextFrame)36 Test (org.junit.Test)34 ArrayList (java.util.ArrayList)31 WebSocketFrame (org.eclipse.jetty.websocket.common.WebSocketFrame)29 CloseInfo (org.eclipse.jetty.websocket.common.CloseInfo)26 Fuzzer (org.eclipse.jetty.websocket.common.test.Fuzzer)26 StacklessLogging (org.eclipse.jetty.util.log.StacklessLogging)20 PingFrame (org.eclipse.jetty.websocket.common.frames.PingFrame)11 PongFrame (org.eclipse.jetty.websocket.common.frames.PongFrame)9 ByteBuffer (java.nio.ByteBuffer)8 BinaryFrame (org.eclipse.jetty.websocket.common.frames.BinaryFrame)4 DataFrame (org.eclipse.jetty.websocket.common.frames.DataFrame)4 Slow (org.eclipse.jetty.toolchain.test.annotation.Slow)3 IncomingFramesCapture (org.eclipse.jetty.websocket.common.test.IncomingFramesCapture)3 UnitParser (org.eclipse.jetty.websocket.common.test.UnitParser)3 Frame (org.eclipse.jetty.websocket.api.extensions.Frame)2 CloseFrame (org.eclipse.jetty.websocket.common.frames.CloseFrame)2 ProtocolException (org.eclipse.jetty.websocket.api.ProtocolException)1 ExtensionConfig (org.eclipse.jetty.websocket.api.extensions.ExtensionConfig)1