Esempio n. 1
0
        protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List <object> output)
        {
            // Discard all data received if closing handshake was received before.
            if (this.receivedClosingHandshake)
            {
                _ = input.SkipBytes(this.ActualReadableBytes);
                return;
            }

            switch (this.state)
            {
            case State.ReadingFirst:
                if (!input.IsReadable())
                {
                    return;
                }

                this.framePayloadLength = 0;

                // FIN, RSV, OPCODE
                byte b = input.ReadByte();
                this.frameFinalFlag = (b & 0x80) != 0;
                this.frameRsv       = (b & 0x70) >> 4;
                this.frameOpcode    = b & 0x0F;

#if DEBUG
                if (Logger.TraceEnabled)
                {
                    Logger.DecodingWebSocketFrameOpCode(this.frameOpcode);
                }
#endif

                this.state = State.ReadingSecond;
                goto case State.ReadingSecond;

            case State.ReadingSecond:
                if (!input.IsReadable())
                {
                    return;
                }

                // MASK, PAYLOAD LEN 1
                b = input.ReadByte();
                this.frameMasked      = (b & 0x80) != 0;
                this.framePayloadLen1 = b & 0x7F;

                if (this.frameRsv != 0 && !_config.AllowExtensions)
                {
                    this.ProtocolViolation_RSVNoExtensionNegotiated(context, input, this.frameRsv);
                    return;
                }

                if (!_config.AllowMaskMismatch && _config.ExpectMaskedFrames != this.frameMasked)
                {
                    this.ProtocolViolation_RecAFrameThatIsNotMaskedAsExected(context, input);
                    return;
                }

                // control frame (have MSB in opcode set)
                if (this.frameOpcode > 7)
                {
                    // control frames MUST NOT be fragmented
                    if (!this.frameFinalFlag)
                    {
                        this.ProtocolViolation_FragmentedControlFrame(context, input);
                        return;
                    }

                    // control frames MUST have payload 125 octets or less
                    if (this.framePayloadLen1 > 125)
                    {
                        this.ProtocolViolation_ControlFrameWithPayloadLength125Octets(context, input);
                        return;
                    }

                    switch (this.frameOpcode)
                    {
                    // 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
                    case OpcodeClose:
                        if (this.framePayloadLen1 == 1)
                        {
                            this.ProtocolViolation_RecCloseControlFrame(context, input);
                            return;
                        }
                        break;

                    case OpcodePing:
                    case OpcodePong:
                        break;

                    // check for reserved control frame opcodes
                    default:
                        this.ProtocolViolation_ControlFrameUsingReservedOpcode(context, input, this.frameOpcode);
                        return;
                    }
                }
                else     // data frame
                {
                    switch (this.frameOpcode)
                    {
                    case OpcodeCont:
                    case OpcodeText:
                    case OpcodeBinary:
                        break;

                    // check for reserved data frame opcodes
                    default:
                        this.ProtocolViolation_DataFrameUsingReservedOpcode(context, input, this.frameOpcode);
                        return;
                    }

                    uint uFragmentedFramesCount = (uint)this.fragmentedFramesCount;
                    // check opcode vs message fragmentation state 1/2
                    if (0u >= uFragmentedFramesCount && this.frameOpcode == OpcodeCont)
                    {
                        this.ProtocolViolation_RecContionuationDataFrame(context, input);
                        return;
                    }

                    // check opcode vs message fragmentation state 2/2
                    if (uFragmentedFramesCount > 0u && this.frameOpcode != OpcodeCont && this.frameOpcode != OpcodePing)
                    {
                        this.ProtocolViolation_RecNonContionuationDataFrame(context, input);
                        return;
                    }
                }

                this.state = State.ReadingSize;
                goto case State.ReadingSize;

            case State.ReadingSize:
                // Read frame payload length
                switch (this.framePayloadLen1)
                {
                case 126:
                    if (input.ReadableBytes < 2)
                    {
                        return;
                    }
                    this.framePayloadLength = input.ReadUnsignedShort();
                    if (this.framePayloadLength < 126)
                    {
                        this.ProtocolViolation_InvalidDataFrameLength(context, input);
                        return;
                    }
                    break;

                case 127:
                    if (input.ReadableBytes < 8)
                    {
                        return;
                    }
                    this.framePayloadLength = input.ReadLong();
                    // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe
                    // just check if it's negative?

                    if (this.framePayloadLength < 65536)
                    {
                        this.ProtocolViolation_InvalidDataFrameLength(context, input);
                        return;
                    }
                    break;

                default:
                    this.framePayloadLength = this.framePayloadLen1;
                    break;
                }

                var maxFramePayloadLength = _config.MaxFramePayloadLength;
                if (this.framePayloadLength > maxFramePayloadLength)
                {
                    this.ProtocolViolation_MaxFrameLengthHasBeenExceeded(context, input, maxFramePayloadLength);
                    return;
                }

#if DEBUG
                if (Logger.TraceEnabled)
                {
                    Logger.DecodingWebSocketFrameLength(this.framePayloadLength);
                }
#endif

                this.state = State.MaskingKey;
                goto case State.MaskingKey;

            case State.MaskingKey:
                if (this.frameMasked)
                {
                    if (input.ReadableBytes < 4)
                    {
                        return;
                    }
                    if (this.maskingKey is null)
                    {
                        this.maskingKey = new byte[4];
                    }
                    _ = input.ReadBytes(this.maskingKey);
                }
                this.state = State.Payload;
                goto case State.Payload;

            case State.Payload:
                if (input.ReadableBytes < this.framePayloadLength)
                {
                    return;
                }

                IByteBuffer payloadBuffer = null;
                try
                {
                    payloadBuffer = ReadBytes(context.Allocator, input, ToFrameLength(this.framePayloadLength));

                    // Now we have all the data, the next checkpoint must be the next
                    // frame
                    this.state = State.ReadingFirst;

                    // Unmask data if needed
                    if (this.frameMasked)
                    {
                        this.Unmask(payloadBuffer);
                    }

                    // Processing ping/pong/close frames because they cannot be
                    // fragmented
                    switch (this.frameOpcode)
                    {
                    case OpcodePing:
                        output.Add(new PingWebSocketFrame(this.frameFinalFlag, this.frameRsv, payloadBuffer));
                        payloadBuffer = null;
                        return;

                    case OpcodePong:
                        output.Add(new PongWebSocketFrame(this.frameFinalFlag, this.frameRsv, payloadBuffer));
                        payloadBuffer = null;
                        return;

                    case OpcodeClose:
                        this.receivedClosingHandshake = true;
                        this.CheckCloseFrameBody(context, payloadBuffer);
                        output.Add(new CloseWebSocketFrame(this.frameFinalFlag, this.frameRsv, payloadBuffer));
                        payloadBuffer = null;
                        return;
                    }

                    // Processing for possible fragmented messages for text and binary
                    // frames
                    if (this.frameFinalFlag)
                    {
                        // Final frame of the sequence. Apparently ping frames are
                        // allowed in the middle of a fragmented message
                        if (this.frameOpcode != OpcodePing)
                        {
                            this.fragmentedFramesCount = 0;
                        }
                    }
                    else
                    {
                        // Increment counter
                        this.fragmentedFramesCount++;
                    }

                    // Return the frame
                    switch (this.frameOpcode)
                    {
                    case OpcodeText:
                        output.Add(new TextWebSocketFrame(this.frameFinalFlag, this.frameRsv, payloadBuffer));
                        payloadBuffer = null;
                        return;

                    case OpcodeBinary:
                        output.Add(new BinaryWebSocketFrame(this.frameFinalFlag, this.frameRsv, payloadBuffer));
                        payloadBuffer = null;
                        return;

                    case OpcodeCont:
                        output.Add(new ContinuationWebSocketFrame(this.frameFinalFlag, this.frameRsv, payloadBuffer));
                        payloadBuffer = null;
                        return;

                    default:
                        ThrowNotSupportedException(this.frameOpcode); return;
                    }
                }
                finally
                {
                    _ = (payloadBuffer?.Release());
                }

            case State.Corrupt:
                if (input.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.
                    _ = input.ReadByte();
                }
                return;

            default:
                ThrowHelper.ThrowException_FrameDecoder(); break;
            }
        }