diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java index f5a6dc1936..05f7780f14 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java @@ -150,234 +150,234 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - // Discard all data received if closing handshake was received before. if (receivedClosingHandshake) { in.skipBytes(actualReadableBytes()); return; } - switch (state) { - case READING_FIRST: - if (!in.isReadable()) { - return; - } - framePayloadLength = 0; - - // FIN, RSV, OPCODE - byte b = in.readByte(); - frameFinalFlag = (b & 0x80) != 0; - frameRsv = (b & 0x70) >> 4; - frameOpcode = b & 0x0F; - - if (logger.isDebugEnabled()) { - logger.debug("Decoding WebSocket Frame opCode={}", frameOpcode); - } - - state = State.READING_SECOND; - case READING_SECOND: - if (!in.isReadable()) { - return; - } - // MASK, PAYLOAD LEN 1 - b = in.readByte(); - frameMasked = (b & 0x80) != 0; - framePayloadLen1 = b & 0x7F; - - if (frameRsv != 0 && !allowExtensions) { - protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); - return; - } - - if (!allowMaskMismatch && expectMaskedFrames != frameMasked) { - protocolViolation(ctx, "received a frame that is not masked as expected"); - return; - } - - if (frameOpcode > 7) { // control frame (have MSB in opcode set) - - // control frames MUST NOT be fragmented - if (!frameFinalFlag) { - protocolViolation(ctx, "fragmented control frame"); - return; - } - - // control frames MUST have payload 125 octets or less - if (framePayloadLen1 > 125) { - protocolViolation(ctx, "control frame with payload length > 125 octets"); - return; - } - - // check for reserved control frame opcodes - if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING - || frameOpcode == OPCODE_PONG)) { - protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode); - return; - } - - // close frame : if there is a body, the first two bytes of the - // body MUST be a 2-byte unsigned integer (in network byte - // order) representing a getStatus code - if (frameOpcode == 8 && framePayloadLen1 == 1) { - protocolViolation(ctx, "received close control frame with payload len 1"); - return; - } - } else { // data frame - // check for reserved data frame opcodes - if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT - || frameOpcode == OPCODE_BINARY)) { - protocolViolation(ctx, "data frame using reserved opcode " + frameOpcode); - return; - } - - // check opcode vs message fragmentation state 1/2 - if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { - protocolViolation(ctx, "received continuation data frame outside fragmented message"); - return; - } - - // check opcode vs message fragmentation state 2/2 - if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { - protocolViolation(ctx, - "received non-continuation data frame while inside fragmented message"); - return; - } - } - - state = State.READING_SIZE; - case READING_SIZE: - - // Read frame payload length - if (framePayloadLen1 == 126) { - if (in.readableBytes() < 2) { - return; - } - framePayloadLength = in.readUnsignedShort(); - if (framePayloadLength < 126) { - protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); - return; - } - } else if (framePayloadLen1 == 127) { - if (in.readableBytes() < 8) { - return; - } - framePayloadLength = in.readLong(); - // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe - // just check if it's negative? - - if (framePayloadLength < 65536) { - protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); - return; - } - } else { - framePayloadLength = framePayloadLen1; - } - - if (framePayloadLength > maxFramePayloadLength) { - protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded."); - return; - } - - if (logger.isDebugEnabled()) { - logger.debug("Decoding WebSocket Frame length={}", framePayloadLength); - } - - state = State.MASKING_KEY; - case MASKING_KEY: - if (frameMasked) { - if (in.readableBytes() < 4) { - return; - } - if (maskingKey == null) { - maskingKey = new byte[4]; - } - in.readBytes(maskingKey); - } - state = State.PAYLOAD; - case PAYLOAD: - if (in.readableBytes() < framePayloadLength) { - return; - } - - ByteBuf payloadBuffer = null; - try { - payloadBuffer = readBytes(ctx.alloc(), in, toFrameLength(framePayloadLength)); - - // Now we have all the data, the next checkpoint must be the next - // frame - state = State.READING_FIRST; - - // Unmask data if needed - if (frameMasked) { - unmask(payloadBuffer); - } - - // Processing ping/pong/close frames because they cannot be - // fragmented - if (frameOpcode == OPCODE_PING) { - out.add(new PingWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); - payloadBuffer = null; - return; - } - if (frameOpcode == OPCODE_PONG) { - out.add(new PongWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); - payloadBuffer = null; - return; - } - if (frameOpcode == OPCODE_CLOSE) { - receivedClosingHandshake = true; - checkCloseFrameBody(ctx, payloadBuffer); - out.add(new CloseWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); - payloadBuffer = null; - return; - } - - // Processing for possible fragmented messages for text and binary - // frames - if (frameFinalFlag) { - // Final frame of the sequence. Apparently ping frames are - // allowed in the middle of a fragmented message - if (frameOpcode != OPCODE_PING) { - fragmentedFramesCount = 0; - } - } else { - // Increment counter - fragmentedFramesCount++; - } - - // Return the frame - if (frameOpcode == OPCODE_TEXT) { - out.add(new TextWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); - payloadBuffer = null; - return; - } else if (frameOpcode == OPCODE_BINARY) { - out.add(new BinaryWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); - payloadBuffer = null; - return; - } else if (frameOpcode == OPCODE_CONT) { - out.add(new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, - payloadBuffer)); - payloadBuffer = null; - return; - } else { - throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " - + frameOpcode); - } - } finally { - if (payloadBuffer != null) { - payloadBuffer.release(); - } - } - case CORRUPT: - if (in.isReadable()) { - // If we don't keep reading Netty will throw an exception saying - // we can't return null if no bytes read and state not changed. - in.readByte(); - } - return; - default: - throw new Error("Shouldn't reach here."); + switch (state) { + case READING_FIRST: + if (!in.isReadable()) { + return; } + + framePayloadLength = 0; + + // FIN, RSV, OPCODE + byte b = in.readByte(); + frameFinalFlag = (b & 0x80) != 0; + frameRsv = (b & 0x70) >> 4; + frameOpcode = b & 0x0F; + + if (logger.isDebugEnabled()) { + logger.debug("Decoding WebSocket Frame opCode={}", frameOpcode); + } + + state = State.READING_SECOND; + case READING_SECOND: + if (!in.isReadable()) { + return; + } + // MASK, PAYLOAD LEN 1 + b = in.readByte(); + frameMasked = (b & 0x80) != 0; + framePayloadLen1 = b & 0x7F; + + if (frameRsv != 0 && !allowExtensions) { + protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); + return; + } + + if (!allowMaskMismatch && expectMaskedFrames != frameMasked) { + protocolViolation(ctx, "received a frame that is not masked as expected"); + return; + } + + if (frameOpcode > 7) { // control frame (have MSB in opcode set) + + // control frames MUST NOT be fragmented + if (!frameFinalFlag) { + protocolViolation(ctx, "fragmented control frame"); + return; + } + + // control frames MUST have payload 125 octets or less + if (framePayloadLen1 > 125) { + protocolViolation(ctx, "control frame with payload length > 125 octets"); + return; + } + + // check for reserved control frame opcodes + if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING + || frameOpcode == OPCODE_PONG)) { + protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode); + return; + } + + // close frame : if there is a body, the first two bytes of the + // body MUST be a 2-byte unsigned integer (in network byte + // order) representing a getStatus code + if (frameOpcode == 8 && framePayloadLen1 == 1) { + protocolViolation(ctx, "received close control frame with payload len 1"); + return; + } + } else { // data frame + // check for reserved data frame opcodes + if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT + || frameOpcode == OPCODE_BINARY)) { + protocolViolation(ctx, "data frame using reserved opcode " + frameOpcode); + return; + } + + // check opcode vs message fragmentation state 1/2 + if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { + protocolViolation(ctx, "received continuation data frame outside fragmented message"); + return; + } + + // check opcode vs message fragmentation state 2/2 + if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { + protocolViolation(ctx, + "received non-continuation data frame while inside fragmented message"); + return; + } + } + + state = State.READING_SIZE; + case READING_SIZE: + + // Read frame payload length + if (framePayloadLen1 == 126) { + if (in.readableBytes() < 2) { + return; + } + framePayloadLength = in.readUnsignedShort(); + if (framePayloadLength < 126) { + protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); + return; + } + } else if (framePayloadLen1 == 127) { + if (in.readableBytes() < 8) { + return; + } + framePayloadLength = in.readLong(); + // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe + // just check if it's negative? + + if (framePayloadLength < 65536) { + protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); + return; + } + } else { + framePayloadLength = framePayloadLen1; + } + + if (framePayloadLength > maxFramePayloadLength) { + protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded."); + return; + } + + if (logger.isDebugEnabled()) { + logger.debug("Decoding WebSocket Frame length={}", framePayloadLength); + } + + state = State.MASKING_KEY; + case MASKING_KEY: + if (frameMasked) { + if (in.readableBytes() < 4) { + return; + } + if (maskingKey == null) { + maskingKey = new byte[4]; + } + in.readBytes(maskingKey); + } + state = State.PAYLOAD; + case PAYLOAD: + if (in.readableBytes() < framePayloadLength) { + return; + } + + ByteBuf payloadBuffer = null; + try { + payloadBuffer = readBytes(ctx.alloc(), in, toFrameLength(framePayloadLength)); + + // Now we have all the data, the next checkpoint must be the next + // frame + state = State.READING_FIRST; + + // Unmask data if needed + if (frameMasked) { + unmask(payloadBuffer); + } + + // Processing ping/pong/close frames because they cannot be + // fragmented + if (frameOpcode == OPCODE_PING) { + out.add(new PingWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); + payloadBuffer = null; + return; + } + if (frameOpcode == OPCODE_PONG) { + out.add(new PongWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); + payloadBuffer = null; + return; + } + if (frameOpcode == OPCODE_CLOSE) { + receivedClosingHandshake = true; + checkCloseFrameBody(ctx, payloadBuffer); + out.add(new CloseWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); + payloadBuffer = null; + return; + } + + // Processing for possible fragmented messages for text and binary + // frames + if (frameFinalFlag) { + // Final frame of the sequence. Apparently ping frames are + // allowed in the middle of a fragmented message + if (frameOpcode != OPCODE_PING) { + fragmentedFramesCount = 0; + } + } else { + // Increment counter + fragmentedFramesCount++; + } + + // Return the frame + if (frameOpcode == OPCODE_TEXT) { + out.add(new TextWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); + payloadBuffer = null; + return; + } else if (frameOpcode == OPCODE_BINARY) { + out.add(new BinaryWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer)); + payloadBuffer = null; + return; + } else if (frameOpcode == OPCODE_CONT) { + out.add(new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, + payloadBuffer)); + payloadBuffer = null; + return; + } else { + throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " + + frameOpcode); + } + } finally { + if (payloadBuffer != null) { + payloadBuffer.release(); + } + } + case CORRUPT: + if (in.isReadable()) { + // If we don't keep reading Netty will throw an exception saying + // we can't return null if no bytes read and state not changed. + in.readByte(); + } + return; + default: + throw new Error("Shouldn't reach here."); + } } private void unmask(ByteBuf frame) {