System.Net.WebSockets.ManagedWebSocket.CloseAsyncPrivate C# (CSharp) Method

CloseAsyncPrivate() private method

Send a close message, then receive until we get a close response message.
private CloseAsyncPrivate ( WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken ) : Task
closeStatus WebSocketCloseStatus The close status to send.
statusDescription string The close status description to send.
cancellationToken System.Threading.CancellationToken The CancellationToken to use to cancel the websocket.
return Task
        private async Task CloseAsyncPrivate(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
        {
            // Send the close message.  Skip sending a close frame if we're currently in a CloseSent state,
            // for example having just done a CloseOutputAsync.
            if (!_sentCloseFrame)
            {
                await SendCloseFrameAsync(closeStatus, statusDescription, cancellationToken).ConfigureAwait(false);
            }

            // We should now either be in a CloseSent case (because we just sent one), or in a CloseReceived state, in case
            // there was a concurrent receive that ended up handling an immediate close frame response from the server.
            // Of course it could also be Aborted if something happened concurrently to cause things to blow up.
            Debug.Assert(
                State == WebSocketState.CloseSent ||
                State == WebSocketState.CloseReceived ||
                State == WebSocketState.Aborted,
                $"Unexpected state {State}.");

            // Wait until we've received a close response
            byte[] closeBuffer = new byte[MaxMessageHeaderLength + MaxControlPayloadLength];
            while (!_receivedCloseFrame)
            {
                Debug.Assert(!Monitor.IsEntered(StateUpdateLock), $"{nameof(StateUpdateLock)} must never be held when acquiring {nameof(ReceiveAsyncLock)}");
                Task<WebSocketReceiveResult> receiveTask;
                lock (ReceiveAsyncLock)
                {
                    // Now that we're holding the ReceiveAsyncLock, double-check that we've not yet received the close frame.
                    // It could have been received between our check above and now due to a concurrent receive completing.
                    if (_receivedCloseFrame)
                    {
                        break;
                    }

                    // We've not yet processed a received close frame, which means we need to wait for a received close to complete.
                    // There may already be one in flight, in which case we want to just wait for that one rather than kicking off
                    // another (we don't support concurrent receive operations).  We need to kick off a new receive if either we've
                    // never issued a receive or if the last issued receive completed for reasons other than a close frame.  There is
                    // a race condition here, e.g. if there's a in-flight receive that completes after we check, but that's fine: worst
                    // case is we then await it, find that it's not what we need, and try again.
                    receiveTask = _lastReceiveAsync;
                    if (receiveTask == null ||
                        (receiveTask.Status == TaskStatus.RanToCompletion && receiveTask.Result.MessageType != WebSocketMessageType.Close))
                    {
                        _lastReceiveAsync = receiveTask = ReceiveAsyncPrivate(new ArraySegment<byte>(closeBuffer), cancellationToken);
                    }
                }

                // Wait for whatever receive task we have.  We'll then loop around again to re-check our state.
                Debug.Assert(receiveTask != null);
                await receiveTask.ConfigureAwait(false);
            }

            // We're closed.  Close the connection and update the status.
            lock (StateUpdateLock)
            {
                DisposeCore();
                if (_state < WebSocketState.Closed)
                {
                    _state = WebSocketState.Closed;
                }
            }
        }