/// <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 WebSocketFrame Read(Stream fromStream, ArraySegment <byte> intoBuffer) { // allocate a small buffer to read small chunks of data from the stream var smallBuffer = new ArraySegment <byte>(new byte[8]); BinaryReaderWriter.ReadExactly(2, fromStream, smallBuffer); 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 = ReadLength(byte2, smallBuffer, fromStream); int count = (int)len; try { // use the masking key to decode the data if needed if (isMaskBitSet) { ArraySegment <byte> maskKey = new ArraySegment <byte>(smallBuffer.Array, 0, WebSocketFrameExtensions.MaskKeyLength); BinaryReaderWriter.ReadExactly(maskKey.Count, fromStream, maskKey); BinaryReaderWriter.ReadExactly(count, fromStream, intoBuffer); ArraySegment <byte> payloadToMask = new ArraySegment <byte>(intoBuffer.Array, intoBuffer.Offset, count); WebSocketFrameExtensions.ToggleMask(maskKey, payloadToMask); } else { BinaryReaderWriter.ReadExactly(count, fromStream, intoBuffer); } } catch (InternalBufferOverflowException e) { throw new InternalBufferOverflowException($"Supplied buffer too small to read {0} bytes from {Enum.GetName(typeof(WebSocketOpCode), opCode)} frame", e); } 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[WebSocketFrameExtensions.MaskKeyLength]; _random.NextBytes(maskKey); memoryStream.Write(maskKey, 0, maskKey.Length); // mask the payload ArraySegment <byte> maskKeyArraySegment = new ArraySegment <byte>(maskKey, 0, maskKey.Length); WebSocketFrameExtensions.ToggleMask(maskKeyArraySegment, fromPayload); } memoryStream.Write(fromPayload.Array, fromPayload.Offset, fromPayload.Count); }