예제 #1
0
 public void CloseFrameReceivedInUnexpectedState(Guid guid, WebSocketState webSocketState, WebSocketCloseStatus?closeStatus, string statusDescription)
 {
     if (this.IsEnabled())
     {
         string closeStatusDesc = $"{closeStatus}";
         WriteEvent(34, guid, webSocketState, closeStatusDesc, statusDescription ?? string.Empty);
     }
 }
예제 #2
0
 public WebSocketClosedEventArgs(WebSocketCloseStatus?status, string description, Exception exception)
 {
     Status      = status;
     Description = description;
     Exception   = exception;
 }
예제 #3
0
 /// <summary>
 /// Fires with the connection with the client has closed and after OnClose
 /// </summary>
 public virtual Task OnCloseAsync(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
     return(Task.Delay(0));
 }
예제 #4
0
 public WebSocketStringResult(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus?closeStatus, string closeStatusDescription) : base(count, messageType, endOfMessage, closeStatus, closeStatusDescription)
 {
 }
예제 #5
0
 public CloseMessage(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
     CloseStatus            = closeStatus;
     CloseStatusDescription = closeStatusDescription;
 }
예제 #6
0
        /// <summary>Processes a received close message.</summary>
        /// <param name="header">The message header.</param>
        /// <param name="cancellationToken">The cancellation token to use to cancel the websocket.</param>
        /// <returns>The received result message.</returns>
        private async Task<WebSocketReceiveResult> HandleReceivedCloseAsync(
            MessageHeader header, CancellationToken cancellationToken)
        {
            lock (StateUpdateLock)
            {
                _receivedCloseFrame = true;
                if (_state < WebSocketState.CloseReceived)
                {
                    _state = WebSocketState.CloseReceived;
                }
            }

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

            // Handle any payload by parsing it into the close status and description.
            if (header.PayloadLength == 1)
            {
                // The close payload length can be 0 or >= 2, but not 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);
                }

                if (_isServer)
                {
                    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 = s_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);
            }

            // Store the close status and description onto the instance.
            _closeStatus = closeStatus;
            _closeStatusDescription = closeStatusDescription;

            // And return them as part of the result message.
            return new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, closeStatus, closeStatusDescription);
        }
예제 #7
0
 public WebSocketMessage(WebSocketCloseStatus?status, string closeStatDesc)
 {
     WebSocketCloseStatus = status;
     CloseStatDesc        = closeStatDesc;
 }
 public WebSocketReceivedResultEventArgs(WebSocketCloseStatus?closeStatus, string closeStatDesc)
 {
     this.CloseStatus          = closeStatus;
     this.CloseStatDescription = closeStatDesc;
 }
예제 #9
0
 private void InvokeOnMessage(OpCode opCode, WebSocketCloseStatus?status, ArraySegment <byte> buffer)
 => OnMessage?.Invoke(opCode, status, buffer);
 public override Task OnCloseAsync(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
     return(_connection.OnCloseAsync?.Invoke() ?? Task.FromResult(true));
 }
        public override Task OnCloseAsync(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
        {
            Server.Stop();

            return(base.OnCloseAsync(closeStatus, closeStatusDescription));
        }
 public override void OnClose(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
     logger.Info("ContainerProcessHandler.OnClose", new Dictionary <string, object> {
         { "closeStatus", closeStatus.ToString() }, { "closeStatusDescription", closeStatusDescription }
     });
 }
예제 #13
0
 public WebSocketClosedException(string message, Exception innerException, WebSocketCloseStatus?closeStatus = null) : base(message, innerException)
 {
     CloseStatus = closeStatus;
 }
예제 #14
0
 public static WebSocketMessage Close(WebSocketCloseStatus?status)
 => new WebSocketMessage
 {
     Type        = InternalWebsocketMessageType.Close,
     CloseStatus = status
 };
예제 #15
0
 public static void ClientClosed(ILogger logger, WebSocketCloseStatus?closeStatus, string closeDescription)
 {
     _clientClosed(logger, closeStatus, closeDescription, null);
 }
 protected override void DisconnectOccurred(WebSocketCloseStatus?result)
 {
     this.Connected = this.Authenticated = false;
     base.DisconnectOccurred(result);
 }
예제 #17
0
        public override async Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            bool callClose = false;
            lock (_stateLock)
            {
                callClose = (_state != WebSocketState.CloseSent) && (_state != WebSocketState.Closed);
            }

            InterlockedCheckAndUpdateCloseState(WebSocketState.CloseSent, s_validCloseStates);

            using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken))
            {
                if (callClose)
                {
                    _messageWebSocket.Close((ushort) closeStatus, statusDescription ?? String.Empty);
                }

                var result = await _closeWebSocketReceiveResultTcs.Task;
                _closeStatus = result.CloseStatus;
                _closeStatusDescription = result.CloseStatusDescription;
                InterlockedCheckAndUpdateCloseState(WebSocketState.CloseReceived, s_validCloseStates);
            }
        }
예제 #18
0
        public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
        {
            ThrowIfDisposed();
            ThrowIfInputClosed();
            ValidateSegment(buffer);

            var result = await _session.ReceiveAsync(buffer, cancellationToken);

            if (result.MessageType == WebSocketMessageType.Close)
            {
                _closeStatusDescription = result.CloseStatusDescription;
                _closeStatus = result.CloseStatus;

                if (State == WebSocketState.Open)
                {
                    _state = WebSocketState.CloseReceived;
                }
                else if (State == WebSocketState.CloseSent)
                {
                    _state = WebSocketState.Closed;
                    _session.WebSocketDispose();
                }
            }
            return result;
        }
예제 #19
0
 internal static CloseReason FromCloseFrame(WebSocketCloseStatus?closeStatus, string?closeDescription, string?additionalDescription, ShutdownRequest?shutdownRequest)
 {
     return(new CloseReason(null, closeStatus, closeDescription, additionalDescription, shutdownRequest));
 }
예제 #20
0
 public override void OnClose(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
     logger.Info("OnClose: {0} :: {1}", closeStatus.ToString(), closeStatusDescription);
 }
예제 #21
0
 // 연결이 끊어진 경우 retry 여부
 private void OnDisconnected(WebSocketCloseStatus?status)
 {
     Log.Debug("connection lost!");
 }
예제 #22
0
 public WebSocketReceiveResult(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
     if (count < 0)
     {
         throw new ArgumentOutOfRangeException("count");
     }
     this.Count                  = count;
     this.EndOfMessage           = endOfMessage;
     this.MessageType            = messageType;
     this.CloseStatus            = closeStatus;
     this.CloseStatusDescription = closeStatusDescription;
 }
예제 #23
0
        private async Task ConnectAsyncJavaScript(Uri uri, CancellationToken cancellationToken)
        {
            var tcsConnect = new TaskCompletionSource <bool> ();

            // For Abort/Dispose.  Calling Abort on the request at any point will close the connection.
            cts.Token.Register(AbortRequest);

            // Wrap the cancellationToken in a using so that it can be disposed of whether
            // we successfully connected or failed trying.
            // Otherwise any timeout/cancellation would apply to the full session.
            // In the failure case we need to release the references and dispose of the objects.
            using (cancellationToken.Register(() => tcsConnect.TrySetCanceled())) {
                try {
                    Core.Array subProtocols = null;
                    if (Options.RequestedSubProtocols.Count > 0)
                    {
                        subProtocols = new Core.Array();
                        foreach (var item in Options.RequestedSubProtocols)
                        {
                            subProtocols.Push(item);
                        }
                    }
                    innerWebSocket = new HostObject("WebSocket", uri.ToString(), subProtocols);

                    subProtocols?.Dispose();

                    // Setup the onError callback
                    onError = new Action <JSObject> ((errorEvt) => {
                        errorEvt.Dispose();
                    });

                    // Attach the onError callback
                    innerWebSocket.SetObjectProperty("onerror", onError);

                    // Setup the onClose callback
                    onClose = new Action <JSObject> ((closeEvt) => {
                        innerWebSocketCloseStatus            = (WebSocketCloseStatus)closeEvt.GetObjectProperty("code");
                        innerWebSocketCloseStatusDescription = closeEvt.GetObjectProperty("reason")?.ToString();
                        var mess = new ReceivePayload(WebSocketHelpers.EmptyPayload, WebSocketMessageType.Close);
                        receiveMessageQueue.BufferPayload(mess);

                        if (!tcsConnect.Task.IsCanceled && !tcsConnect.Task.IsCompleted && !tcsConnect.Task.IsFaulted)
                        {
                            tcsConnect.SetException(new WebSocketException(WebSocketError.NativeError));
                        }
                        else
                        {
                            tcsClose?.SetResult(true);
                        }

                        closeEvt.Dispose();
                    });

                    // Attach the onClose callback
                    innerWebSocket.SetObjectProperty("onclose", onClose);

                    // Setup the onOpen callback
                    onOpen = new Action <JSObject> ((evt) => {
                        if (!cancellationToken.IsCancellationRequested)
                        {
                            // Change internal state to 'connected' to enable the other methods
                            if (Interlocked.CompareExchange(ref state, connected, connecting) != connecting)
                            {
                                // Aborted/Disposed during connect.
                                throw new ObjectDisposedException(GetType().FullName);
                            }

                            tcsConnect.SetResult(true);
                        }

                        evt.Dispose();
                    });

                    // Attach the onOpen callback
                    innerWebSocket.SetObjectProperty("onopen", onOpen);

                    // Setup the onMessage callback
                    onMessage = new Action <JSObject> ((messageEvent) => {
                        ThrowIfNotConnected();

                        // get the events "data"
                        var eventData = messageEvent.GetObjectProperty("data");

                        // If the messageEvent's data property is marshalled as a JSObject then we are dealing with
                        // binary data
                        if (eventData is JSObject)
                        {
                            // TODO: Handle ArrayBuffer binary type but have only seen 'blob' so far without
                            // changing the default websocket binary type manually.
                            if (innerWebSocket.GetObjectProperty("binaryType").ToString() == "blob")
                            {
                                Action <JSObject> loadend = null;
                                // Create a new "FileReader" object
                                using (var reader = new HostObject("FileReader")) {
                                    loadend = new Action <JSObject> ((loadEvent) => {
                                        using (var target = (JSObject)loadEvent.GetObjectProperty("target")) {
                                            if ((int)target.GetObjectProperty("readyState") == 2)
                                            {
                                                using (var binResult = (ArrayBuffer)target.GetObjectProperty("result")) {
                                                    var mess = new ReceivePayload(binResult, WebSocketMessageType.Binary);
                                                    receiveMessageQueue.BufferPayload(mess);
                                                    Runtime.FreeObject(loadend);
                                                }
                                            }
                                        }
                                        loadEvent.Dispose();
                                    });

                                    reader.Invoke("addEventListener", "loadend", loadend);

                                    using (var blobData = (JSObject)messageEvent.GetObjectProperty("data"))
                                        reader.Invoke("readAsArrayBuffer", blobData);
                                }
                            }
                            else
                            {
                                throw new NotImplementedException($"WebSocket bynary type '{innerWebSocket.GetObjectProperty ("binaryType").ToString ()}' not supported.");
                            }
                        }
                        else if (eventData is string)
                        {
                            var mess = new ReceivePayload(Encoding.UTF8.GetBytes(((string)eventData).ToString()), WebSocketMessageType.Text);
                            receiveMessageQueue.BufferPayload(mess);
                        }
                        messageEvent.Dispose();
                    });

                    // Attach the onMessage callaback
                    innerWebSocket.SetObjectProperty("onmessage", onMessage);

                    await tcsConnect.Task;
                } catch (Exception wse) {
                    ConnectExceptionCleanup();
                    WebSocketException wex = new WebSocketException("WebSocket connection failure.", wse);
                    throw wex;
                }
            }
        }
예제 #24
0
        private async Task<WebSocketReceiveResult> ProcessCloseFrameAsync(CancellationToken cancellationToken)
        {
            // The close message should be less than 125 bytes and fit in the buffer.
            await EnsureDataAvailableOrReadAsync((int)_frameBytesRemaining, CancellationToken.None);

            // Status code and message are optional
            if (_frameBytesRemaining >= 2)
            {
                if (_unmaskInput)
                {
                    Utilities.MaskInPlace(_frameInProgress.MaskKey, new ArraySegment<byte>(_receiveBuffer, _receiveBufferOffset, (int)_frameBytesRemaining));
                }
                _closeStatus = (WebSocketCloseStatus)((_receiveBuffer[_receiveBufferOffset] << 8) | _receiveBuffer[_receiveBufferOffset + 1]);
                _closeStatusDescription = Encoding.UTF8.GetString(_receiveBuffer, _receiveBufferOffset + 2, (int)_frameBytesRemaining - 2) ?? string.Empty;
            }
            else
            {
                _closeStatus = _closeStatus ?? WebSocketCloseStatus.NormalClosure;
                _closeStatusDescription = _closeStatusDescription ?? string.Empty;
            }

            Contract.Assert(_frameInProgress.Fin);
            WebSocketReceiveResult result = new WebSocketReceiveResult(0, WebSocketMessageType.Close, _frameInProgress.Fin,
                _closeStatus.Value, _closeStatusDescription);

            if (State == WebSocketState.Open)
            {
                _state = WebSocketState.CloseReceived;
            }
            else if (State == WebSocketState.CloseSent)
            {
                _state = WebSocketState.Closed;
                _stream.Dispose();
            }

            return result;
        }
예제 #25
0
 public extern WebSocketReceiveResult(int count, WebSocketMessageType messageType, bool endOfMessage,
                                      WebSocketCloseStatus?closeStatus, string closeStatusDescription);
예제 #26
0
 public override void OnClose(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
     DisconnectFromChatSession(sessionId);
 }
예제 #27
0
 /// <summary>
 /// Fires with the connection with the client has closed
 /// </summary>
 public virtual void OnClose(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
 }
예제 #28
0
 public static partial void ClientClosed(ILogger logger, WebSocketCloseStatus?status, string description);
 public ValueWebSocketReceiveResult GetResult(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus?closeStatus, string closeDescription) =>
 new ValueWebSocketReceiveResult(count, messageType, endOfMessage);     // closeStatus/closeDescription are ignored
예제 #30
0
 public override void OnClose(WebSocketCloseStatus?closeStatus, string closeStatusDescription)
 {
     OnCloseCalled    = true;
     CloseStatus      = closeStatus;
     CloseDescription = closeStatusDescription;
 }
예제 #31
0
 public static void WebSocketClosed(ILogger logger, WebSocketCloseStatus?closeStatus)
 {
     _webSocketClosed(logger, closeStatus, null);
 }
예제 #32
0
        private async Task<WebSocketReceiveResult> ProcessCloseFrameAsync(CancellationToken cancellationToken)
        {
            // The close message should be less than 125 bytes and fit in the buffer.
            await EnsureDataAvailableOrReadAsync((int)_frameBytesRemaining, CancellationToken.None);

            // Status code and message are optional
            if (_frameBytesRemaining >= 2)
            {
                if (_unmaskInput)
                {
                    Utilities.MaskInPlace(_frameInProgress.MaskKey, new ArraySegment<byte>(_receiveBuffer, _receiveBufferOffset, (int)_frameBytesRemaining));
                }
                _closeStatus = (WebSocketCloseStatus)((_receiveBuffer[_receiveBufferOffset] << 8) | _receiveBuffer[_receiveBufferOffset + 1]);
                if (!ValidateCloseStatus(_closeStatus.Value))
                {
                    await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid close status code.", cancellationToken);
                }
                try
                {
                    var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
                    _closeStatusDescription = encoding.GetString(_receiveBuffer, _receiveBufferOffset + 2, (int)_frameBytesRemaining - 2) ?? string.Empty;
                }
                catch (DecoderFallbackException)
                {
                    await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid UTF-8 close message.", cancellationToken);
                }
            }
            else if (_frameBytesRemaining == 1)
            {
                await SendErrorAbortAndThrow(WebSocketCloseStatus.ProtocolError, "Invalid close body.", cancellationToken);
            }
            else
            {
                _closeStatus = _closeStatus ?? WebSocketCloseStatus.NormalClosure;
                _closeStatusDescription = _closeStatusDescription ?? string.Empty;
            }

            Contract.Assert(_frameInProgress.Fin);
            WebSocketReceiveResult result = new WebSocketReceiveResult(0, WebSocketMessageType.Close, _frameInProgress.Fin,
                _closeStatus.Value, _closeStatusDescription);

            if (State == WebSocketState.Open)
            {
                _state = WebSocketState.CloseReceived;
            }
            else if (State == WebSocketState.CloseSent)
            {
                _state = WebSocketState.Closed;
                _stream.Dispose();
            }

            return result;
        }
예제 #33
0
 public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
 {
     ThrowIfDisposed();
     ThrowIfInputClosed();
     ValidateSegment(buffer);
     // TODO: InvalidOperationException if any receives are currently in progress.
     
     Message receiveMessage = _receiveMessage;
     _receiveMessage = null;
     if (receiveMessage == null)
     {
         receiveMessage = await _receiveBuffer.ReceiveAsync(cancellationToken);
     }
     if (receiveMessage.MessageType == WebSocketMessageType.Close)
     {
         _closeStatus = receiveMessage.CloseStatus;
         _closeStatusDescription = receiveMessage.CloseStatusDescription ?? string.Empty;
         var result = new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, _closeStatus, _closeStatusDescription);
         if (_state == WebSocketState.Open)
         {
             _state = WebSocketState.CloseReceived;
         }
         else if (_state == WebSocketState.CloseSent)
         {
             _state = WebSocketState.Closed;
             Close();
         }
         return result;
     }
     else
     {
         int count = Math.Min(buffer.Count, receiveMessage.Buffer.Count);
         bool endOfMessage = count == receiveMessage.Buffer.Count;
         Array.Copy(receiveMessage.Buffer.Array, receiveMessage.Buffer.Offset, buffer.Array, buffer.Offset, count);
         if (!endOfMessage)
         {
             receiveMessage.Buffer = new ArraySegment<byte>(receiveMessage.Buffer.Array, receiveMessage.Buffer.Offset + count, receiveMessage.Buffer.Count - count);
             _receiveMessage = receiveMessage;
         }
         endOfMessage = endOfMessage && receiveMessage.EndOfMessage;
         return new WebSocketReceiveResult(count, receiveMessage.MessageType, endOfMessage);
     }
 }
        public void ConstructorTest_Success(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus?closeStatus, string closeStatusDescription)
        {
            var wsrr = new WebSocketReceiveResult(count, messageType, endOfMessage, closeStatus, closeStatusDescription);

            Assert.Equal(wsrr.Count, count);
            Assert.Equal(wsrr.MessageType, messageType);
            Assert.Equal(wsrr.EndOfMessage, endOfMessage);
            Assert.Equal(wsrr.CloseStatus, closeStatus);
            Assert.Equal(wsrr.CloseStatusDescription, closeStatusDescription);
        }
예제 #35
0
        public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer,
            CancellationToken cancellationToken)
        {
            InterlockedCheckValidStates(s_validReceiveStates);

            using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken))
            {
                _webSocketReceiveResultTcs = new TaskCompletionSource<WebSocketReceiveResult>();
                _receiveAsyncBufferTcs.TrySetResult(buffer);

                Task<WebSocketReceiveResult> completedTask = await Task.WhenAny(
                    _webSocketReceiveResultTcs.Task,
                    _closeWebSocketReceiveResultTcs.Task);
                WebSocketReceiveResult result = await completedTask;

                if (result.MessageType == WebSocketMessageType.Close)
                {
                    _closeStatus = result.CloseStatus;
                    _closeStatusDescription = result.CloseStatusDescription;
                    InterlockedCheckAndUpdateCloseState(WebSocketState.CloseReceived, s_validCloseOutputStates);
                }
                else
                {
                    _webSocketReceiveResultTcs = new TaskCompletionSource<WebSocketReceiveResult>();
                }

                InterlockedCheckValidStates(s_validAfterReceiveStates);
                return result;
            }
        }
예제 #36
0
 public WebSocketClosedEventArgs(string clientid, WebSocketCloseStatus?res, string closeStatDesc) : base(res, closeStatDesc)
 {
     ClientId = clientid;
 }
예제 #37
0
        private void UpdateServerCloseStatus()
        {
            uint ret;
            ushort serverStatus;
            var closeDescription = new byte[WebSocketValidate.MaxControlFramePayloadLength];
            uint closeDescriptionConsumed;

            lock (_operation.Lock)
            {
                ret = Interop.WinHttp.WinHttpWebSocketQueryCloseStatus(
                    _operation.WebSocketHandle,
                    out serverStatus,
                    closeDescription,
                    (uint)closeDescription.Length,
                    out closeDescriptionConsumed);

                if (ret != Interop.WinHttp.ERROR_SUCCESS)
                {
                    throw WinHttpException.CreateExceptionUsingError((int)ret);
                }

                _closeStatus = (WebSocketCloseStatus)serverStatus;
                _closeStatusDescription = Encoding.UTF8.GetString(closeDescription, 0, (int)closeDescriptionConsumed);
            }
        }
            public override async Task <WebSocketReceiveResult> ReceiveAsync(ArraySegment <byte> buffer, CancellationToken cancellationToken)
            {
                try
                {
                    while (_internalBuffer.Buffer == null || _internalBuffer.Buffer.Length == 0)
                    {
                        await _input.WaitToReadAsync(cancellationToken).ConfigureAwait(false);

                        if (_input.TryRead(out var message))
                        {
                            if (message.MessageType == WebSocketMessageType.Close)
                            {
                                _state                  = WebSocketState.CloseReceived;
                                _closeStatus            = message.CloseStatus;
                                _closeStatusDescription = message.CloseStatusDescription;
                                return(new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, message.CloseStatus, message.CloseStatusDescription));
                            }

                            _internalBuffer = message;
                        }
                        else
                        {
                            await Task.Delay(100).ConfigureAwait(false);
                        }
                    }

                    var length = _internalBuffer.Buffer.Length;
                    if (buffer.Count < _internalBuffer.Buffer.Length)
                    {
                        length = Math.Min(buffer.Count, _internalBuffer.Buffer.Length);
                        Buffer.BlockCopy(_internalBuffer.Buffer, 0, buffer.Array, buffer.Offset, length);
                    }
                    else
                    {
                        Buffer.BlockCopy(_internalBuffer.Buffer, 0, buffer.Array, buffer.Offset, length);
                    }

                    var endOfMessage = _internalBuffer.EndOfMessage;
                    if (length > 0)
                    {
                        // Remove the sent bytes from the remaining buffer
                        _internalBuffer.Buffer = _internalBuffer.Buffer.AsMemory().Slice(length).ToArray();
                        endOfMessage           = _internalBuffer.Buffer.Length == 0 && endOfMessage;
                    }

                    return(new WebSocketReceiveResult(length, _internalBuffer.MessageType, endOfMessage));
                }
                catch (WebSocketException ex)
                {
                    switch (ex.WebSocketErrorCode)
                    {
                    case WebSocketError.ConnectionClosedPrematurely:
                        _state = WebSocketState.Aborted;
                        break;
                    }

                    // Complete the client side if there's an error
                    _output.TryComplete();

                    throw;
                }

                throw new InvalidOperationException("Unexpected close");
            }