/// <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);
            }
        }
Beispiel #2
0
        /// <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));
        }
        /// <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);
        }