private EncodeResult EncodeFrame(ProtocolFramework.Buffer data, out ProtocolFramework.Buffer encoded, WebSocketOpCode opCode)
        {
            int size = data == null ? 0 : data.Size;

            encoded = new ProtocolFramework.Buffer(2 + 8 + size); // max possible length of header

            const bool isLastFrame = true;

            byte finBitSetAsByte = isLastFrame ? (byte)0x80 : (byte)0x00;
            byte byte1           = (byte)(finBitSetAsByte | (byte)opCode);

            encoded.Append(byte1);

            if (size < 126)
            {
                encoded.Append((byte)size);
            }
            else if (size <= ushort.MaxValue)
            {
                encoded.Append(126);

                byte[] buffer = BitConverter.GetBytes((ushort)size);
                Array.Reverse(buffer);
                encoded.Append(buffer, 0, buffer.Length);
            }
            else
            {
                encoded.Append(127);

                byte[] buffer = BitConverter.GetBytes((ulong)size);
                Array.Reverse(buffer);
                encoded.Append(buffer, 0, buffer.Length);
            }

            if (data != null)
            {
                encoded.Append(data);
            }

            return(EncodeResult.Success);
        }
        public override DecodeResult TryDecode(object connectionToken, out ProtocolFramework.Buffer decodedBytes, out ProtocolFramework.Buffer outputBytes)
        {
            decodedBytes = null;
            outputBytes  = null;
            ConnectionToken token = (ConnectionToken)connectionToken;

            if (token.PendingData.Empty())
            {
                return(DecodeResult.WantMoreData);
            }

            if (!token.IsNegociationEnded)
            {
                return(this.TryDecodeHandshakeData(token, out decodedBytes, out outputBytes));
            }

            // We start interpreting two bytes.
            int headerSize = 2;

            if (token.PendingData.Size < headerSize)
            {
                return(DecodeResult.WantMoreData);
            }

            // First byte = Fin (1 bit) + Flags (3 bits) + opcodes (4 bits):
            byte       byte1            = token.PendingData.Data[0];
            const byte finBitFlag       = 0x80;
            const byte opCodeFlag       = 0x0F;
            const byte reservedBitsFlag = 0x70;

            bool isFinBitSet  = (byte1 & finBitFlag) == finBitFlag;
            int  reservedBits = (byte1 & reservedBitsFlag);

            if (reservedBits != 0)
            {
                Console.WriteLine("Reserved bits not zeros");
                return(DecodeResult.Failure);
            }

            WebSocketOpCode opCode = (WebSocketOpCode)(byte1 & opCodeFlag);

            if (!WebsocketProtocol.IsValidCode(opCode))
            {
                Console.WriteLine("Invalid opCode: " + opCode);
                return(DecodeResult.Failure);
            }


            // Second byte is Mask (1 bit) + Payload Size (7 bits)
            byte       byte2          = token.PendingData.Data[1];
            const byte maskFlag       = 0x80;
            bool       isMaskBitSet   = (byte2 & maskFlag) == maskFlag;
            const byte payloadLenFlag = 0x7F;
            uint       len            = (uint)(byte2 & payloadLenFlag);

            int payloadSize = 0;

            if (len <= 125)
            {
                payloadSize = (int)len;
            }
            if (len == 126)
            {
                headerSize += 2;
                if (token.PendingData.Size < headerSize)
                {
                    return(DecodeResult.WantMoreData);
                }

                byte[] lenBuffer = new byte[2];
                Array.Copy(token.PendingData.Data, 2, lenBuffer, 0, 2);
                Array.Reverse(lenBuffer); // big endian
                payloadSize = (int)BitConverter.ToUInt16(lenBuffer, 0);
            }
            else if (len == 127)
            {
                headerSize += 8;
                if (token.PendingData.Size < headerSize)
                {
                    return(DecodeResult.WantMoreData);
                }

                byte[] lenBuffer = new byte[8];
                Array.Copy(token.PendingData.Data, 2, lenBuffer, 0, 8);
                Array.Reverse(lenBuffer); // big endian
                payloadSize = (int)BitConverter.ToUInt64(lenBuffer, 0);
            }

            if (WebsocketProtocol.IsControlFrame(opCode) && payloadSize > 125)
            {
                Console.WriteLine("Large control frame payload");
                return(DecodeResult.Failure);
            }

            int totalSize = headerSize + (isMaskBitSet ? 4 : 0) + (int)payloadSize;

            if (token.PendingData.Size < totalSize)
            {
                return(DecodeResult.WantMoreData);
            }

            ProtocolFramework.Buffer payloadBuffer = new ProtocolFramework.Buffer(payloadSize);
            int payloadOffset = headerSize + (isMaskBitSet ? 4 : 0);

            payloadBuffer.Append(token.PendingData.Data, payloadOffset, (int)payloadSize);

            if (isMaskBitSet)
            {
                const int maskKeyLen = 4;
                byte[]    maskKey    = new byte[4];
                Array.Copy(token.PendingData.Data, headerSize, maskKey, 0, 4);
                // apply the mask key
                for (int i = 0; i < payloadBuffer.Size; i++)
                {
                    payloadBuffer.Data[i] = (Byte)(payloadBuffer.Data[i] ^ maskKey[i % maskKeyLen]);
                }
            }

            token.PendingData.PopAndAdjust(payloadOffset + (int)payloadSize);

            if (opCode == WebSocketOpCode.ConnectionClose)
            {
                this.EncodeFrame(null, out outputBytes, WebSocketOpCode.ConnectionClose);
                return(DecodeResult.Failure); // TODO.
            }
            else if (opCode == WebSocketOpCode.Pong)
            {
                return(DecodeResult.Success);
            }
            else if (opCode == WebSocketOpCode.Ping)
            {
                this.EncodeFrame(payloadBuffer, out outputBytes, WebSocketOpCode.Pong);
                return(DecodeResult.Success);
            }
            else
            {
                token.LastReceivedOpCode = opCode;
                decodedBytes             = payloadBuffer;
                return(DecodeResult.Success);
            }
        }