public async ValueTask <Stream> ReceiveAsync(CancellationToken cancellationToken)
        {
            ThrowIfDisposed();

            await _receiveSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

            try
            {
                _receiveStream.Position = 0;
                _receiveStream.SetLength(0);
                do
                {
                    // See _limboCts for more info on cancellation.
                    var receiveTask = _ws.ReceiveAsync(_receiveBuffer, _limboCts.Token).AsTask();
                    using (var infiniteCts = Cts.Linked(cancellationToken))
                    {
                        var infiniteTask = Task.Delay(Timeout.Infinite, infiniteCts.Token);
                        var task         = await Task.WhenAny(infiniteTask, receiveTask).ConfigureAwait(false);

                        infiniteCts.Cancel();
                    }

                    if (cancellationToken.IsCancellationRequested)
                    {
                        throw new OperationCanceledException(cancellationToken);
                    }

                    var result = await receiveTask.ConfigureAwait(false);

                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        var closeStatus  = _ws.CloseStatus;
                        var closeMessage = _ws.CloseMessage;
                        try
                        {
                            await _ws.CloseOutputAsync(closeStatus.GetValueOrDefault(), closeMessage, default).ConfigureAwait(false);
                        }
                        catch { }

                        throw new WebSocketClosedException(closeStatus, closeMessage);
                    }

                    _receiveStream.Write(_receiveBuffer.AsSpan(0, result.Count));
                    if (!result.EndOfMessage)
                    {
                        continue;
                    }

                    if (result.MessageType != WebSocketMessageType.Binary)
                    {
                        _receiveStream.Position = 0;
                        return(_receiveStream);
                    }

                    _receiveStream.TryGetBuffer(out var streamBuffer);

                    // We check the data for the ZLib flush which marks the end of the actual message.
                    if (streamBuffer.Count < 4 || BinaryPrimitives.ReadUInt32BigEndian(streamBuffer[^ 4..]) != 0x0000FFFF)
Exemple #2
0
        public async ValueTask <Stream> ReceiveAsync(CancellationToken cancellationToken)
        {
            ThrowIfDisposed();

            await _receiveSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

            try
            {
                // Ensures that the receive stream is fully read and the underlying DeflateStream acknowledges the ZLib suffix.
                if (_supportsZLib && _wasLastPayloadZLib && _receiveStream.Position != _receiveStream.Length)
                {
                    // We just need the inflater to read further so that it picks up the suffix and knows it's done.
                    _receiveZLibStream.Read(Array.Empty <byte>());
                }

                _receiveStream.Position = 0;
                _receiveStream.SetLength(0);
                do
                {
                    // See _limboCts for more info on cancellation.
                    var receiveTask = _ws.ReceiveAsync(_receiveBuffer, _limboCts.Token).AsTask();
                    using (var infiniteCts = Cts.Linked(cancellationToken))
                    {
                        var infiniteTask = Task.Delay(Timeout.Infinite, infiniteCts.Token);
                        var task         = await Task.WhenAny(infiniteTask, receiveTask).ConfigureAwait(false);

                        infiniteCts.Cancel();
                    }

                    if (cancellationToken.IsCancellationRequested)
                    {
                        throw new OperationCanceledException(cancellationToken);
                    }

                    var result = await receiveTask.ConfigureAwait(false);

                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        var closeStatus  = _ws.CloseStatus;
                        var closeMessage = _ws.CloseMessage;
                        try
                        {
                            await _ws.CloseOutputAsync(closeStatus.GetValueOrDefault(), closeMessage, default).ConfigureAwait(false);
                        }
                        catch { }

                        throw new WebSocketClosedException(closeStatus, closeMessage);
                    }

                    _receiveStream.Write(_receiveBuffer.AsSpan(0, result.Count));
                    if (!result.EndOfMessage)
                    {
                        continue;
                    }

                    if (result.MessageType != WebSocketMessageType.Binary)
                    {
                        _wasLastPayloadZLib     = false;
                        _receiveStream.Position = 0;
                        return(_receiveStream);
                    }

                    _receiveStream.TryGetBuffer(out var streamBuffer);

                    // We check the data for the ZLib flush which marks the end of the actual message.
                    if (streamBuffer.Count < 4 || BinaryPrimitives.ReadUInt32BigEndian(streamBuffer[^ 4..]) != 0x0000FFFF)