public RawFrameData Get()
        {
            if (Data == null)
            {
                Data = VariableSizedBufferPool.NoData;
            }

            using (var ms = new BufferPoolMemoryStream(this.DataLength + 9))
            {
                // For the complete documentation for this section see:
                // http://tools.ietf.org/html/rfc6455#section-5.2

                // Write the header
                ms.WriteByte(this.Header);

                // The length of the "Payload data", in bytes: if 0-125, that is the payload length.  If 126, the following 2 bytes interpreted as a
                // 16-bit unsigned integer are the payload length.  If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the
                // most significant bit MUST be 0) are the payload length.  Multibyte length quantities are expressed in network byte order.
                if (this.DataLength < 126)
                {
                    ms.WriteByte((byte)(0x80 | (byte)this.DataLength));
                }
                else if (this.DataLength < UInt16.MaxValue)
                {
                    ms.WriteByte((byte)(0x80 | 126));
                    byte[] len = BitConverter.GetBytes((UInt16)this.DataLength);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(len, 0, len.Length);
                    }

                    ms.Write(len, 0, len.Length);
                }
                else
                {
                    ms.WriteByte((byte)(0x80 | 127));
                    byte[] len = BitConverter.GetBytes((UInt64)this.DataLength);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(len, 0, len.Length);
                    }

                    ms.Write(len, 0, len.Length);
                }

                // All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame.  This field is
                // present if the mask bit is set to 1 and is absent if the mask bit is set to 0.
                // If the data is being sent by the client, the frame(s) MUST be masked.
                byte[] mask = BitConverter.GetBytes((Int32)this.GetHashCode());
                ms.Write(mask, 0, mask.Length);

                // Do the masking.
                for (int i = 0; i < this.DataLength; ++i)
                {
                    ms.WriteByte((byte)(Data[i] ^ mask[i % 4]));
                }

                return(new RawFrameData(ms.ToArray(true), (int)ms.Length));
            }
        }
Beispiel #2
0
        private string DecodeString(Stream stream)
        {
            byte   start        = (byte)stream.ReadByte();
            bool   rawString    = BufferHelper.ReadBit(start, 0) == 0;
            UInt32 stringLength = DecodeInteger(7, start, stream);

            if (rawString)
            {
                byte[] buffer = BufferPool.Get(stringLength, true);

                stream.Read(buffer, 0, (int)stringLength);

                BufferPool.Release(buffer);

                return(System.Text.Encoding.UTF8.GetString(buffer, 0, (int)stringLength));
            }
            else
            {
                var  node        = HuffmanEncoder.GetRoot();
                byte currentByte = (byte)stream.ReadByte();
                byte bitIdx      = 0; // 0..7

                using (BufferPoolMemoryStream bufferStream = new BufferPoolMemoryStream())
                {
                    do
                    {
                        byte bitValue = BufferHelper.ReadBit(currentByte, bitIdx);

                        if (++bitIdx > 7)
                        {
                            stringLength--;

                            if (stringLength > 0)
                            {
                                bitIdx      = 0;
                                currentByte = (byte)stream.ReadByte();
                            }
                        }

                        node = HuffmanEncoder.GetNext(node, bitValue);

                        if (node.Value != 0)
                        {
                            if (node.Value != HuffmanEncoder.EOS)
                            {
                                bufferStream.WriteByte((byte)node.Value);
                            }

                            node = HuffmanEncoder.GetRoot();
                        }
                    } while (stringLength > 0);

                    byte[] buffer = bufferStream.ToArray(true);

                    string result = System.Text.Encoding.UTF8.GetString(buffer, 0, (int)bufferStream.Length);

                    BufferPool.Release(buffer);

                    return(result);
                }
            }
        }
        public BufferSegment EncodeMessage(Message message)
        {
            var memBuffer = BufferPool.Get(256, true);
            var stream    = new BufferPoolMemoryStream(memBuffer, 0, memBuffer.Length, true, true, false, true);

            // Write 5 bytes for placeholder for length prefix
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);

            var bufferWriter = new BufferPoolBufferWriter(stream);
            var writer       = new MessagePackWriter(bufferWriter);

            switch (message.type)
            {
            case MessageTypes.StreamItem:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1
                // [2, Headers, InvocationId, Item]

                writer.WriteArrayHeader(4);

                writer.Write(2);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                WriteValue(ref writer, bufferWriter, message.item);

                break;

            case MessageTypes.Completion:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1
                // [3, Headers, InvocationId, ResultKind, Result?]

                byte resultKind = (byte)(!string.IsNullOrEmpty(message.error) ? /*error*/ 1 : message.result != null ? /*non-void*/ 3 : /*void*/ 2);

                writer.WriteArrayHeader(resultKind == 2 ? 4 : 5);

                writer.Write(3);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                writer.Write(resultKind);

                if (resultKind == 1)     // error
                {
                    WriteString(ref writer, message.error);
                }
                else if (resultKind == 3)     // non-void
                {
                    WriteValue(ref writer, bufferWriter, message.result);
                }

                break;

            case MessageTypes.Invocation:
            // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1
            // [1, Headers, InvocationId, NonBlocking, Target, [Arguments], [StreamIds]]

            case MessageTypes.StreamInvocation:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1
                // [4, Headers, InvocationId, Target, [Arguments], [StreamIds]]

                writer.WriteArrayHeader(message.streamIds != null ? 6 : 5);

                writer.Write((int)message.type);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                WriteString(ref writer, message.target);
                writer.WriteArrayHeader(message.arguments != null ? message.arguments.Length : 0);
                if (message.arguments != null)
                {
                    for (int i = 0; i < message.arguments.Length; ++i)
                    {
                        WriteValue(ref writer, bufferWriter, message.arguments[i]);
                    }
                }

                if (message.streamIds != null)
                {
                    writer.WriteArrayHeader(message.streamIds.Length);

                    for (int i = 0; i < message.streamIds.Length; ++i)
                    {
                        WriteValue(ref writer, bufferWriter, message.streamIds[i]);
                    }
                }

                break;

            case MessageTypes.CancelInvocation:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1
                // [5, Headers, InvocationId]

                writer.WriteArrayHeader(3);

                writer.Write(5);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);

                break;

            case MessageTypes.Ping:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1
                // [6]

                writer.WriteArrayHeader(1);
                writer.Write(6);

                break;

            case MessageTypes.Close:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1
                // [7, Error, AllowReconnect?]

                writer.WriteArrayHeader(string.IsNullOrEmpty(message.error) ? 1 : 2);

                writer.Write(7);
                if (!string.IsNullOrEmpty(message.error))
                {
                    WriteString(ref writer, message.error);
                }

                break;
            }

            writer.Flush();

            // get how much bytes got written to the buffer. This includes the 5 placeholder bytes too.
            int length = (int)stream.Position;

            // this is the length without the 5 placeholder bytes
            int contentLength = length - 5;

            // get the stream's internal buffer. We set the releaseBuffer flag to false, so we can use it safely.
            var buffer = stream.GetBuffer();

            // add varint length prefix
            byte prefixBytes = GetRequiredBytesForLengthPrefix(contentLength);

            WriteLengthAsVarInt(buffer, 5 - prefixBytes, contentLength);

            // return with the final segment
            return(new BufferSegment(buffer, 5 - prefixBytes, contentLength + prefixBytes));
        }
Beispiel #4
0
        public unsafe RawFrameData Get()
        {
            if (Data == null)
            {
                Data = BufferPool.NoData;
            }

            using (var ms = new BufferPoolMemoryStream(this.DataLength + 9))
            {
                // For the complete documentation for this section see:
                // http://tools.ietf.org/html/rfc6455#section-5.2

                // Write the header
                ms.WriteByte(this.Header);

                // The length of the "Payload data", in bytes: if 0-125, that is the payload length.  If 126, the following 2 bytes interpreted as a
                // 16-bit unsigned integer are the payload length.  If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the
                // most significant bit MUST be 0) are the payload length.  Multibyte length quantities are expressed in network byte order.
                if (this.DataLength < 126)
                {
                    ms.WriteByte((byte)(0x80 | (byte)this.DataLength));
                }
                else if (this.DataLength < UInt16.MaxValue)
                {
                    ms.WriteByte((byte)(0x80 | 126));
                    byte[] len = BitConverter.GetBytes((UInt16)this.DataLength);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(len, 0, len.Length);
                    }

                    ms.Write(len, 0, len.Length);
                }
                else
                {
                    ms.WriteByte((byte)(0x80 | 127));
                    byte[] len = BitConverter.GetBytes((UInt64)this.DataLength);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(len, 0, len.Length);
                    }

                    ms.Write(len, 0, len.Length);
                }

                // All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame.  This field is
                // present if the mask bit is set to 1 and is absent if the mask bit is set to 0.
                // If the data is being sent by the client, the frame(s) MUST be masked.
                byte[] mask = BufferPool.Get(4, true);

                int hash = this.GetHashCode();

                mask[0] = (byte)((hash >> 24) & 0xFF);
                mask[1] = (byte)((hash >> 16) & 0xFF);
                mask[2] = (byte)((hash >> 8) & 0xFF);
                mask[3] = (byte)(hash & 0xFF);

                ms.Write(mask, 0, 4);

                // Do the masking.
                fixed(byte *pData = Data, pmask = mask)
                {
                    // Here, instead of byte by byte, we reinterpret cast the data as uints and apply the masking so.
                    // This way, we can mask 4 bytes in one cycle, instead of just 1
                    int localLength = this.DataLength / 4;

                    if (localLength > 0)
                    {
                        uint *upData = (uint *)pData;
                        uint  umask  = *(uint *)pmask;

                        unchecked
                        {
                            for (int i = 0; i < localLength; ++i)
                            {
                                upData[i] = upData[i] ^ umask;
                            }
                        }
                    }

                    // Because data might not be exactly dividable by 4, we have to mask the remaining 0..3 too.
                    int from = localLength * 4;

                    localLength = from + this.DataLength % 4;
                    for (int i = from; i < localLength; ++i)
                    {
                        pData[i] = (byte)(pData[i] ^ pmask[i % 4]);
                    }
                }

                BufferPool.Release(mask);

                ms.Write(Data, 0, DataLength);

                return(new RawFrameData(ms.ToArray(true), (int)ms.Length));
            }
        }