Exemple #1
0
        protected override unsafe void Encode(IChannelHandlerContext ctx, WebSocketFrame msg, List <object> output)
        {
            IByteBuffer data = msg.Content;
            var         mask = stackalloc byte[4];

            byte opcode = 0;

            switch (msg.Opcode)
            {
            case Opcode.Text:
                opcode = OpcodeText;
                break;

            case Opcode.Ping:
                opcode = OpcodePing;
                break;

            case Opcode.Pong:
                opcode = OpcodePong;
                break;

            case Opcode.Close:
                opcode = OpcodeClose;
                break;

            case Opcode.Binary:
                opcode = OpcodeBinary;
                break;

            case Opcode.Cont:
                opcode = OpcodeCont;
                break;

            default:
                ThrowNotSupportedException(msg);
                break;
            }

            int length = data.ReadableBytes;

            if (Logger.TraceEnabled)
            {
                Logger.EncodingWebSocketFrameOpCode(opcode, length);
            }

            int b0 = 0;

            if (msg.IsFinalFragment)
            {
                b0 |= 1 << 7;
            }
            b0 |= msg.Rsv % 8 << 4;
            b0 |= opcode % 128;

            if (opcode == OpcodePing && length > 125)
            {
                ThrowTooLongFrameException(length);
            }

            bool        release = true;
            IByteBuffer buf     = null;

            try
            {
                int maskLength = this.maskPayload ? 4 : 0;
                if (length <= 125)
                {
                    int size = 2 + maskLength;
                    if (this.maskPayload || length <= GatheringWriteThreshold)
                    {
                        size += length;
                    }
                    buf = ctx.Allocator.Buffer(size);
                    _   = buf.WriteByte(b0);
                    byte b = (byte)(this.maskPayload ? 0x80 | (byte)length : (byte)length);
                    _ = buf.WriteByte(b);
                }
                else if (length <= 0xFFFF)
                {
                    int size = 4 + maskLength;
                    if (this.maskPayload || length <= GatheringWriteThreshold)
                    {
                        size += length;
                    }
                    buf = ctx.Allocator.Buffer(size);
                    _   = buf.WriteByte(b0);
                    _   = buf.WriteByte(this.maskPayload ? 0xFE : 126);
                    _   = buf.WriteByte(length.RightUShift(8) & 0xFF);
                    _   = buf.WriteByte(length & 0xFF);
                }
                else
                {
                    int size = 10 + maskLength;
                    if (this.maskPayload || length <= GatheringWriteThreshold)
                    {
                        size += length;
                    }
                    buf = ctx.Allocator.Buffer(size);
                    _   = buf.WriteByte(b0);
                    _   = buf.WriteByte(this.maskPayload ? 0xFF : 127);
                    _   = buf.WriteLong(length);
                }

                // Write payload
                if (this.maskPayload)
                {
                    int intMask = (this.random.Next() * int.MaxValue);

                    // Mask bytes in BE
                    uint unsignedValue = (uint)intMask;
                    *    mask          = (byte)(unsignedValue >> 24);
                    *(mask + 1) = (byte)(unsignedValue >> 16);
                    *(mask + 2) = (byte)(unsignedValue >> 8);
                    *(mask + 3) = (byte)unsignedValue;

                    // Mask in BE
                    _ = buf.WriteInt(intMask);

                    int counter = 0;
                    int i       = data.ReaderIndex;
                    int end     = data.WriterIndex;

                    for (; i + 3 < end; i += 4)
                    {
                        int intData = data.GetInt(i);
                        _ = buf.WriteInt(intData ^ intMask);
                    }
                    for (; i < end; i++)
                    {
                        byte byteData = data.GetByte(i);
                        _ = buf.WriteByte(byteData ^ mask[counter++ % 4]);
                    }
                    output.Add(buf);
                }
                else
                {
                    if (buf.WritableBytes >= data.ReadableBytes)
                    {
                        // merge buffers as this is cheaper then a gathering write if the payload is small enough
                        _ = buf.WriteBytes(data);
                        output.Add(buf);
                    }
                    else
                    {
                        output.Add(buf);
                        output.Add(data.Retain());
                    }
                }
                release = false;
            }
            finally
            {
                if (release)
                {
                    _ = (buf?.Release());
                }
            }
        }