// We received a ping, send a pong in reply
        private async Task SendPongReplyAsync(CancellationToken cancellationToken)
        {
            await _writeLock.WaitAsync(cancellationToken);

            try
            {
                if (State != WebSocketState.Open)
                {
                    // Output closed, discard the pong.
                    return;
                }

                ArraySegment <byte> dataSegment = new ArraySegment <byte>(_receiveBuffer, _receiveBufferOffset, (int)_frameBytesRemaining);
                if (_unmaskInput)
                {
                    // _frameInProgress.Masked == _unmaskInput already verified
                    Utilities.MaskInPlace(_frameInProgress.MaskKey, dataSegment);
                }

                int         mask   = GetNextMask();
                FrameHeader header = new FrameHeader(true, Constants.OpCodes.PongFrame, _maskOutput, mask, _frameBytesRemaining);
                if (_maskOutput)
                {
                    Utilities.MaskInPlace(mask, dataSegment);
                }

                ArraySegment <byte> headerSegment = header.Buffer;
                await _stream.WriteAsync(headerSegment.Array, headerSegment.Offset, headerSegment.Count, cancellationToken);

                await _stream.WriteAsync(dataSegment.Array, dataSegment.Offset, dataSegment.Count, cancellationToken);
            }
            finally
            {
                _writeLock.Release();
            }
        }
        private async Task ReadNextFrameAsync(CancellationToken cancellationToken)
        {
            await EnsureDataAvailableOrReadAsync(2, cancellationToken);

            int frameHeaderSize = FrameHeader.CalculateFrameHeaderSize(_receiveBuffer[_receiveBufferOffset + 1]);

            await EnsureDataAvailableOrReadAsync(frameHeaderSize, cancellationToken);

            _frameInProgress      = new FrameHeader(new ArraySegment <byte>(_receiveBuffer, _receiveBufferOffset, frameHeaderSize));
            _receiveBufferOffset += frameHeaderSize;
            _receiveBufferBytes  -= frameHeaderSize;
            _frameBytesRemaining  = _frameInProgress.DataLength;

            if (_frameInProgress.AreReservedSet())
            {
                await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Unexpected reserved bits set", cancellationToken);
            }

            if (_unmaskInput != _frameInProgress.Masked)
            {
                await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Incorrect masking", cancellationToken);
            }

            if (!ValidateOpCode(_frameInProgress.OpCode))
            {
                await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid opcode: " + _frameInProgress.OpCode, cancellationToken);
            }

            if (_frameInProgress.IsControlFrame)
            {
                if (_frameBytesRemaining > 125)
                {
                    await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid control frame size", cancellationToken);
                }

                if (!_frameInProgress.Fin)
                {
                    await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Fragmented control frame", cancellationToken);
                }

                if (_frameInProgress.OpCode == Constants.OpCodes.PingFrame || _frameInProgress.OpCode == Constants.OpCodes.PongFrame)
                {
                    // Drain it, should be less than 125 bytes
                    await EnsureDataAvailableOrReadAsync((int)_frameBytesRemaining, cancellationToken);

                    if (_frameInProgress.OpCode == Constants.OpCodes.PingFrame)
                    {
                        await SendPongReplyAsync(cancellationToken);
                    }

                    _receiveBufferOffset += (int)_frameBytesRemaining;
                    _receiveBufferBytes  -= (int)_frameBytesRemaining;
                    _frameBytesRemaining  = 0;
                    _frameInProgress      = null;
                }
            }
            else if (_firstDataOpCode.HasValue && _frameInProgress.OpCode != Constants.OpCodes.ContinuationFrame)
            {
                // A data frame is already in progress, but this new frame is not a continuation frame.
                await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Expected a continuation frame: " + _frameInProgress.OpCode, cancellationToken);
            }
        }
        public async override Task <WebSocketReceiveResult> ReceiveAsync(ArraySegment <byte> buffer, CancellationToken cancellationToken)
        {
            ThrowIfDisposed();
            ThrowIfInputClosed();
            ValidateSegment(buffer);
            // TODO: InvalidOperationException if any receives are currently in progress.

            // No active frame. Loop because we may be discarding ping/pong frames.
            while (_frameInProgress == null)
            {
                await ReadNextFrameAsync(cancellationToken);
            }

            int opCode = _frameInProgress.OpCode;

            if (opCode == Constants.OpCodes.CloseFrame)
            {
                return(await ProcessCloseFrameAsync(cancellationToken));
            }

            // Handle fragmentation, remember the first frame type
            if (opCode == Constants.OpCodes.ContinuationFrame)
            {
                if (!_firstDataOpCode.HasValue)
                {
                    await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid continuation frame", cancellationToken);
                }
                opCode = _firstDataOpCode.Value;
            }
            else
            {
                _firstDataOpCode = opCode;
            }

            // Make sure there's at least some data in the buffer
            int bytesToBuffer = (int)Math.Min((long)_receiveBuffer.Length, _frameBytesRemaining);

            await EnsureDataAvailableOrReadAsync(bytesToBuffer, cancellationToken);

            // Copy buffered data to the users buffer
            int bytesToRead = (int)Math.Min((long)buffer.Count, _frameBytesRemaining);
            int bytesToCopy = Math.Min(bytesToRead, _receiveBufferBytes);

            Array.Copy(_receiveBuffer, _receiveBufferOffset, buffer.Array, buffer.Offset, bytesToCopy);

            if (_unmaskInput)
            {
                // _frameInProgress.Masked == _unmaskInput already verified
                Utilities.MaskInPlace(_frameInProgress.MaskKey, ref _dataUnmaskOffset, new ArraySegment <byte>(buffer.Array, buffer.Offset, bytesToCopy));
            }

            WebSocketReceiveResult result;
            WebSocketMessageType   messageType = Utilities.GetMessageType(opCode);

            if (messageType == WebSocketMessageType.Text &&
                !Utilities.TryValidateUtf8(new ArraySegment <byte>(buffer.Array, buffer.Offset, bytesToCopy), _frameInProgress.Fin, _incomingUtf8MessageState))
            {
                await SendErrorAbortAndThrow(WebSocketCloseStatus.InvalidPayloadData, "Invalid UTF-8", cancellationToken);
            }

            if (bytesToCopy == _frameBytesRemaining)
            {
                result = new WebSocketReceiveResult(bytesToCopy, messageType, _frameInProgress.Fin);
                if (_frameInProgress.Fin)
                {
                    _firstDataOpCode = null;
                }
                _frameInProgress  = null;
                _dataUnmaskOffset = 0;
            }
            else
            {
                result = new WebSocketReceiveResult(bytesToCopy, messageType, false);
            }

            _frameBytesRemaining -= bytesToCopy;
            _receiveBufferBytes  -= bytesToCopy;
            _receiveBufferOffset += bytesToCopy;

            return(result);
        }