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)); } }
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)); }
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)); } }