/// <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); var byte1 = smallBuffer.Array[0]; var byte2 = smallBuffer.Array[1]; // process first byte byte finBitFlag = 0x80; byte opCodeFlag = 0x0F; var isFinBitSet = (byte1 & finBitFlag) == finBitFlag; var opCode = (WebSocketOpCode)(byte1 & opCodeFlag); // read and process second byte byte maskFlag = 0x80; var isMaskBitSet = (byte2 & maskFlag) == maskFlag; var len = await ReadLength(byte2, smallBuffer, fromStream, cancellationToken); var count = (int)len; try { // use the masking key to decode the data if needed if (isMaskBitSet) { var maskKey = new ArraySegment <byte>(smallBuffer.Array, 0, WebSocketFrameCommon.MaskKeyLength); await BinaryReaderWriter.ReadExactly(maskKey.Count, fromStream, maskKey, cancellationToken); await BinaryReaderWriter.ReadExactly(count, fromStream, intoBuffer, cancellationToken); var 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(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)); }
/// <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 var maskKeyArraySegment = new ArraySegment <byte>(maskKey, 0, maskKey.Length); WebSocketFrameCommon.ToggleMask(maskKeyArraySegment, fromPayload); memoryStream.Write(fromPayload.Array, fromPayload.Offset, fromPayload.Count); // unmask it so we don't destroy user's data WebSocketFrameCommon.ToggleMask(maskKeyArraySegment, fromPayload); return; } memoryStream.Write(fromPayload.Array, fromPayload.Offset, fromPayload.Count); }