private async Task <WebSocketReceiveResult> HandleReceivedCloseAsync(CompressionWebSocketMessageHeader header, CancellationToken cancellationToken)
        {
            lock (StateUpdateLock)
            {
                _receivedCloseFrame = true;
                if (_state < WebSocketState.CloseReceived)
                {
                    _state = WebSocketState.CloseReceived;
                }
            }

            WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure;
            string closeStatusDescription    = string.Empty;

            if (header.PayloadLength == 1)
            {
                await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false);
            }
            else if (header.PayloadLength >= 2)
            {
                if (_receiveBufferCount < header.PayloadLength)
                {
                    await EnsureBufferContainsAsync((int)header.PayloadLength, cancellationToken).ConfigureAwait(false);
                }

                ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, 0, header.PayloadLength);

                closeStatus = (WebSocketCloseStatus)(_receiveBuffer[_receiveBufferOffset] << 8 | _receiveBuffer[_receiveBufferOffset + 1]);
                if (!IsValidCloseStatus(closeStatus))
                {
                    await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false);
                }

                if (header.PayloadLength > 2)
                {
                    try
                    {
                        closeStatusDescription = _textEncoding.GetString(_receiveBuffer, _receiveBufferOffset + 2, (int)header.PayloadLength - 2);
                    }
                    catch (DecoderFallbackException exc)
                    {
                        await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken, exc).ConfigureAwait(false);
                    }
                }
                ConsumeFromBuffer((int)header.PayloadLength);
            }

            _closeStatus            = closeStatus;
            _closeStatusDescription = closeStatusDescription;

            return(new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, closeStatus, closeStatusDescription));
        }
        private async Task HandleReceivedPingPongAsync(CompressionWebSocketMessageHeader header, CancellationToken cancellationToken)
        {
            if (header.PayloadLength > 0 && _receiveBufferCount < header.PayloadLength)
            {
                await EnsureBufferContainsAsync((int)header.PayloadLength, cancellationToken).ConfigureAwait(false);
            }

            if (header.Opcode == WebSocketMessageOpcode.Ping)
            {
                ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, 0, header.PayloadLength);

                await SendFrameAsync(WebSocketMessageOpcode.Pong, false, true, new ArraySegment <byte>(_receiveBuffer, _receiveBufferOffset, (int)header.PayloadLength), cancellationToken).ConfigureAwait(false);
            }

            if (header.PayloadLength > 0)
            {
                ConsumeFromBuffer((int)header.PayloadLength);
            }
        }
        private bool TryParseMessageHeaderFromReceiveBuffer(out CompressionWebSocketMessageHeader resultHeader)
        {
            var header = new CompressionWebSocketMessageHeader();

            header.Fin        = (_receiveBuffer[_receiveBufferOffset] & 0x80) != 0;
            header.Compressed = (_receiveBuffer[_receiveBufferOffset] & 0x40) != 0;
            bool reservedSet = (_receiveBuffer[_receiveBufferOffset] & 0x70) != 0;
            bool reservedExceptCompressedSet = (_receiveBuffer[_receiveBufferOffset] & 0x30) != 0;

            header.Opcode = (WebSocketMessageOpcode)(_receiveBuffer[_receiveBufferOffset] & 0xF);

            bool masked = (_receiveBuffer[_receiveBufferOffset + 1] & 0x80) != 0;

            header.PayloadLength = _receiveBuffer[_receiveBufferOffset + 1] & 0x7F;

            ConsumeFromBuffer(2);

            if (header.PayloadLength == 126)
            {
                header.PayloadLength = (_receiveBuffer[_receiveBufferOffset] << 8) | _receiveBuffer[_receiveBufferOffset + 1];
                ConsumeFromBuffer(2);
            }
            else if (header.PayloadLength == 127)
            {
                header.PayloadLength = 0;
                for (int i = 0; i < 8; i++)
                {
                    header.PayloadLength = (header.PayloadLength << 8) | _receiveBuffer[_receiveBufferOffset + i];
                }
                ConsumeFromBuffer(8);
            }

            bool shouldFail = (!header.Compressed && reservedSet) || reservedExceptCompressedSet;

            if (masked)
            {
                header.Mask = BitConverter.ToInt32(_receiveBuffer, _receiveBufferOffset);
                ConsumeFromBuffer(4);
            }

            switch (header.Opcode)
            {
            case WebSocketMessageOpcode.Continuation:
                if (_lastReceiveHeader.Fin)
                {
                    shouldFail = true;
                }
                break;

            case WebSocketMessageOpcode.Binary:
            case WebSocketMessageOpcode.Text:
                if (!_lastReceiveHeader.Fin)
                {
                    shouldFail = true;
                }
                break;

            case WebSocketMessageOpcode.Close:
            case WebSocketMessageOpcode.Ping:
            case WebSocketMessageOpcode.Pong:
                if (header.PayloadLength > MAX_CONTROL_PAYLOAD_LENGTH || !header.Fin)
                {
                    shouldFail = true;
                }
                break;

            default:
                shouldFail = true;
                break;
            }

            resultHeader = header;

            return(!shouldFail);
        }
        private async Task <WebSocketReceiveResult> InternalReceiveAsync(ArraySegment <byte> payloadBuffer, CancellationToken cancellationToken)
        {
            CancellationTokenRegistration registration = cancellationToken.Register(s => ((CompressionWebSocket)s).Abort(), this);

            try
            {
                while (true)
                {
                    CompressionWebSocketMessageHeader header = _lastReceiveHeader;
                    if (header.PayloadLength == 0)
                    {
                        if (_receiveBufferCount < (MAX_MESSAGE_HEADER_LENGTH - MASK_LENGTH))
                        {
                            if (_receiveBufferCount < 2)
                            {
                                await EnsureBufferContainsAsync(2, cancellationToken, throwOnPrematureClosure : true).ConfigureAwait(false);
                            }

                            long payloadLength = _receiveBuffer[_receiveBufferOffset + 1] & 0x7F;
                            int  minNeeded     = 2 + MASK_LENGTH + (payloadLength <= 125 ? 0 : payloadLength == 126 ? sizeof(ushort) : sizeof(ulong));

                            await EnsureBufferContainsAsync(minNeeded, cancellationToken).ConfigureAwait(false);
                        }

                        if (!TryParseMessageHeaderFromReceiveBuffer(out header))
                        {
                            await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false);
                        }
                        _receivedMaskOffsetOffset = 0;
                    }

                    if (header.Opcode == WebSocketMessageOpcode.Ping || header.Opcode == WebSocketMessageOpcode.Pong)
                    {
                        await HandleReceivedPingPongAsync(header, cancellationToken).ConfigureAwait(false);

                        continue;
                    }
                    else if (header.Opcode == WebSocketMessageOpcode.Close)
                    {
                        return(await HandleReceivedCloseAsync(header, cancellationToken).ConfigureAwait(false));
                    }

                    if (header.Opcode == WebSocketMessageOpcode.Continuation)
                    {
                        header.Compressed = _lastReceiveHeader.Compressed;
                        header.Opcode     = _lastReceiveHeader.Opcode;
                    }

                    int bytesToRead = (int)Math.Min(payloadBuffer.Count, header.PayloadLength);
                    if (bytesToRead == 0)
                    {
                        _lastReceiveHeader = header;
                        return(new WebSocketReceiveResult(
                                   0,
                                   header.Opcode == WebSocketMessageOpcode.Text ? WebSocketMessageType.Text : WebSocketMessageType.Binary,
                                   header.PayloadLength == 0 ? header.Fin : false));
                    }

                    if (_receiveBufferCount == 0)
                    {
                        await EnsureBufferContainsAsync(1, cancellationToken, throwOnPrematureClosure : false).ConfigureAwait(false);
                    }

                    int bytesToCopy = Math.Min(bytesToRead, _receiveBufferCount);
                    _receivedMaskOffsetOffset = ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, _receivedMaskOffsetOffset, bytesToCopy);

                    Buffer.BlockCopy(_receiveBuffer, _receiveBufferOffset, payloadBuffer.Array, payloadBuffer.Offset, bytesToCopy);
                    ConsumeFromBuffer(bytesToCopy);
                    header.PayloadLength -= bytesToCopy;

                    if ((header.Opcode == WebSocketMessageOpcode.Text) && !header.Compressed && !TryValidateUtf8(new ArraySegment <byte>(payloadBuffer.Array, payloadBuffer.Offset, bytesToCopy), header.Fin, _utf8TextState))
                    {
                        await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.InvalidPayloadData, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false);
                    }

                    _lastReceiveHeader = header;
                    return(new CompressionWebSocketReceiveResult(
                               bytesToCopy,
                               header.Opcode == WebSocketMessageOpcode.Text ? WebSocketMessageType.Text : WebSocketMessageType.Binary,
                               header.Compressed,
                               bytesToCopy == 0 || (header.Fin && header.PayloadLength == 0)));
                }
            }
            catch (Exception ex)
            {
                throw _state == WebSocketState.Aborted ? new WebSocketException(WebSocketError.InvalidState, "Invalid state.", ex) : new WebSocketException(WebSocketError.ConnectionClosedPrematurely, ex);
            }
            finally
            {
                registration.Dispose();
            }
        }