/// <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(System.IO.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; try { // 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); } } 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> /// The last read could not be completed because the read buffer was too small. /// We need to continue reading bytes off the stream. /// Not to be confused with a continuation frame /// </summary> /// <param name="fromStream">The stream to read from</param> /// <param name="intoBuffer">The buffer to read into</param> /// <param name="readCursor">The previous partial websocket frame read plus cursor information</param> /// <param name="cancellationToken">the cancellation token</param> /// <returns>A websocket frame</returns> public static async Task <WebSocketReadCursor> ReadFromCursorAsync(System.IO.Stream fromStream, ArraySegment <byte> intoBuffer, WebSocketReadCursor readCursor, CancellationToken cancellationToken) { var remainingFrame = readCursor.WebSocketFrame; var minCount = CalculateNumBytesToRead(readCursor.NumBytesLeftToRead, intoBuffer.Count); await BinaryReaderWriter.ReadExactly(minCount, fromStream, intoBuffer, cancellationToken); if (remainingFrame.MaskKey.Count > 0) { ArraySegment <byte> payloadToMask = new ArraySegment <byte>(intoBuffer.Array, intoBuffer.Offset, minCount); WebSocketFrameCommon.ToggleMask(remainingFrame.MaskKey, payloadToMask); } return(new WebSocketReadCursor(remainingFrame, minCount, readCursor.NumBytesLeftToRead - minCount)); }