/// <summary> /// Reads the length of the payload according to the contents of byte2 /// </summary> private static async Task <uint> ReadLength(byte byte2, ArraySegment <byte> smallBuffer, Stream fromStream, CancellationToken cancellationToken) { byte payloadLenFlag = 0x7F; uint len = (uint)(byte2 & payloadLenFlag); // read a short length or a long length depending on the value of len if (len == 126) { len = await BinaryReaderWriter.ReadUShortExactly(fromStream, false, smallBuffer, cancellationToken); } else if (len == 127) { len = (uint)await BinaryReaderWriter.ReadULongExactly(fromStream, false, smallBuffer, cancellationToken); const uint maxLen = 2147483648; // 2GB - not part of the spec but just a precaution. Send large volumes of data in smaller frames. // protect ourselves against bad data if (len > maxLen || len < 0) { throw new ArgumentOutOfRangeException($"Payload length out of range. Min 0 max 2GB. Actual {len:#,##0} bytes."); } } return(len); }
/// <summary> /// Read a WebSocket frame from the stream /// </summary> /// <param name="fromStream">The stream to read from</param> /// <param name="intoBuffer">The buffer to read into</param> /// <param name="cancellationToken">the cancellation token</param> /// <returns>A websocket frame</returns> public static async Task <WebSocketFrame> ReadAsync(Stream fromStream, ArraySegment <byte> intoBuffer, CancellationToken cancellationToken) { // allocate a small buffer to read small chunks of data from the stream var smallBuffer = new ArraySegment <byte>(new byte[8]); await BinaryReaderWriter.ReadExactly(2, fromStream, smallBuffer, cancellationToken); byte byte1 = smallBuffer.Array[0]; byte byte2 = smallBuffer.Array[1]; // process first byte byte finBitFlag = 0x80; byte opCodeFlag = 0x0F; bool isFinBitSet = (byte1 & finBitFlag) == finBitFlag; WebSocketOpCode opCode = (WebSocketOpCode)(byte1 & opCodeFlag); // read and process second byte byte maskFlag = 0x80; bool isMaskBitSet = (byte2 & maskFlag) == maskFlag; uint len = await ReadLength(byte2, smallBuffer, fromStream, cancellationToken); int count = (int)len; // use the masking key to decode the data if needed if (isMaskBitSet) { ArraySegment <byte> maskKey = new ArraySegment <byte>(smallBuffer.Array, 0, WebSocketFrameCommon.MaskKeyLength); await BinaryReaderWriter.ReadExactly(maskKey.Count, fromStream, maskKey, cancellationToken); await BinaryReaderWriter.ReadExactly(count, fromStream, intoBuffer, cancellationToken); ArraySegment <byte> payloadToMask = new ArraySegment <byte>(intoBuffer.Array, intoBuffer.Offset, count); WebSocketFrameCommon.ToggleMask(maskKey, payloadToMask); } else { await BinaryReaderWriter.ReadExactly(count, fromStream, intoBuffer, cancellationToken); } if (opCode == WebSocketOpCode.ConnectionClose) { return(DecodeCloseFrame(isFinBitSet, opCode, count, intoBuffer)); } else { // note that by this point the payload will be populated return(new WebSocketFrame(isFinBitSet, opCode, count)); } }
/// <summary> /// No async await stuff here because we are dealing with a memory stream /// </summary> /// <param name="opCode">The web socket opcode</param> /// <param name="fromPayload">Array segment to get payload data from</param> /// <param name="toStream">Stream to write to</param> /// <param name="isLastFrame">True is this is the last frame in this message (usually true)</param> public static void Write(WebSocketOpCode opCode, ArraySegment <byte> fromPayload, MemoryStream toStream, bool isLastFrame, bool isClient) { MemoryStream memoryStream = toStream; byte finBitSetAsByte = isLastFrame ? (byte)0x80 : (byte)0x00; byte byte1 = (byte)(finBitSetAsByte | (byte)opCode); memoryStream.WriteByte(byte1); // NB, set the mask flag if we are constructing a client frame byte maskBitSetAsByte = isClient ? (byte)0x80 : (byte)0x00; // depending on the size of the length we want to write it as a byte, ushort or ulong if (fromPayload.Count < 126) { byte byte2 = (byte)(maskBitSetAsByte | (byte)fromPayload.Count); memoryStream.WriteByte(byte2); } else if (fromPayload.Count <= ushort.MaxValue) { byte byte2 = (byte)(maskBitSetAsByte | 126); memoryStream.WriteByte(byte2); BinaryReaderWriter.WriteUShort((ushort)fromPayload.Count, memoryStream, false); } else { byte byte2 = (byte)(maskBitSetAsByte | 127); memoryStream.WriteByte(byte2); BinaryReaderWriter.WriteULong((ulong)fromPayload.Count, memoryStream, false); } // if we are creating a client frame then we MUST mack the payload as per the spec if (isClient) { byte[] maskKey = new byte[WebSocketFrameCommon.MaskKeyLength]; _random.NextBytes(maskKey); memoryStream.Write(maskKey, 0, maskKey.Length); // mask the payload ArraySegment <byte> maskKeyArraySegment = new ArraySegment <byte>(maskKey, 0, maskKey.Length); WebSocketFrameCommon.ToggleMask(maskKeyArraySegment, fromPayload); } memoryStream.Write(fromPayload.Array, fromPayload.Offset, fromPayload.Count); }