Ejemplo n.º 1
0
        /// <summary>
        /// Receive web socket result
        /// </summary>
        /// <param name="buffer">The buffer to copy data into</param>
        /// <param name="cancellationToken">The cancellation token</param>
        /// <returns>The web socket result details</returns>
        public override async Task <WebSocketReceiveResult> ReceiveAsync(ArraySegment <byte> buffer, CancellationToken cancellationToken)
        {
            try
            {
                // we may receive control frames so reading needs to happen in an infinite loop
                while (true)
                {
                    // allow this operation to be cancelled from iniside OR outside this instance
                    using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_internalReadCts.Token, cancellationToken))
                    {
                        WebSocketFrame frame = null;
                        try
                        {
                            frame = await WebSocketFrameReader.ReadAsync(_stream, buffer, linkedCts.Token);
                        }
                        catch (InternalBufferOverflowException ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.MessageTooBig, "Frame too large to fit in buffer. Use message fragmentation", ex);

                            throw;
                        }
                        catch (ArgumentOutOfRangeException ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.ProtocolError, "Payload length out of range", ex);

                            throw;
                        }
                        catch (EndOfStreamException ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InvalidPayloadData, "Unexpected end of stream encountered", ex);

                            throw;
                        }
                        catch (OperationCanceledException ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.EndpointUnavailable, "Operation cancelled", ex);

                            throw;
                        }
                        catch (Exception ex)
                        {
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InternalServerError, "Error reading WebSocket frame", ex);

                            throw;
                        }

                        switch (frame.OpCode)
                        {
                        case WebSocketOpCode.ConnectionClose:
                            return(await RespondToCloseFrame(frame, buffer, linkedCts.Token));

                        case WebSocketOpCode.Ping:
                            ArraySegment <byte> pingPayload = new ArraySegment <byte>(buffer.Array, buffer.Offset, frame.Count);
                            await SendPongAsync(pingPayload, linkedCts.Token);

                            break;

                        case WebSocketOpCode.Pong:
                            ArraySegment <byte> pongBuffer = new ArraySegment <byte>(buffer.Array, frame.Count, buffer.Offset);
                            Pong?.Invoke(this, new PongEventArgs(pongBuffer));
                            break;

                        case WebSocketOpCode.TextFrame:
                            if (!frame.IsFinBitSet)
                            {
                                // continuation frames will follow, record the message type Text
                                _continuationFrameMessageType = WebSocketMessageType.Text;
                            }
                            return(new WebSocketReceiveResult(frame.Count, WebSocketMessageType.Text, frame.IsFinBitSet));

                        case WebSocketOpCode.BinaryFrame:
                            if (!frame.IsFinBitSet)
                            {
                                // continuation frames will follow, record the message type Binary
                                _continuationFrameMessageType = WebSocketMessageType.Binary;
                            }
                            return(new WebSocketReceiveResult(frame.Count, WebSocketMessageType.Binary, frame.IsFinBitSet));

                        case WebSocketOpCode.ContinuationFrame:
                            return(new WebSocketReceiveResult(frame.Count, _continuationFrameMessageType, frame.IsFinBitSet));

                        default:
                            Exception ex = new NotSupportedException($"Unknown WebSocket opcode {frame.OpCode}");
                            await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.ProtocolError, ex.Message, ex);

                            throw ex;
                        }
                    }
                }
            }
            catch (Exception catchAll)
            {
                // Most exceptions will be caught closer to their source to send an appropriate close message (and set the WebSocketState)
                // However, if an unhandled exception is encountered and a close message not sent then send one here
                if (_state == WebSocketState.Open)
                {
                    await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InternalServerError, "Unexpected error reading from WebSocket", catchAll);
                }

                throw;
            }
        }
Ejemplo n.º 2
0
        /// <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 <WebSocketReadCursor> 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;
            var minCount = CalculateNumBytesToRead(count, intoBuffer.Count);
            ArraySegment <byte> maskKey = new ArraySegment <byte>();

            try
            {
                // use the masking key to decode the data if needed
                if (isMaskBitSet)
                {
                    maskKey = new ArraySegment <byte>(smallBuffer.Array, 0, WebSocketFrameCommon.MaskKeyLength);
                    await BinaryReaderWriter.ReadExactly(maskKey.Count, fromStream, maskKey, cancellationToken);

                    await BinaryReaderWriter.ReadExactly(minCount, fromStream, intoBuffer, cancellationToken);

                    ArraySegment <byte> payloadToMask =
                        new ArraySegment <byte>(intoBuffer.Array, intoBuffer.Offset, minCount);
                    WebSocketFrameCommon.ToggleMask(maskKey, payloadToMask);
                }
                else
                {
                    await BinaryReaderWriter.ReadExactly(minCount, 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);
            }

            WebSocketFrame frame;

            if (opCode == WebSocketOpCode.ConnectionClose)
            {
                frame = DecodeCloseFrame(isFinBitSet, opCode, count, intoBuffer, maskKey);
            }
            else
            {
                // note that by this point the payload will be populated
                frame = new WebSocketFrame(isFinBitSet, opCode, count, maskKey);
            }

            return(new WebSocketReadCursor(frame, minCount, count - minCount));
        }