System.Net.WebSockets.WebSocketBase.WebSocketOperation.Process C# (CSharp) Method

Process() private method

private Process ( Nullable buffer, CancellationToken cancellationToken ) : Task
buffer Nullable
cancellationToken System.Threading.CancellationToken
return Task
            internal async Task<WebSocketReceiveResult> Process(Nullable<ArraySegment<byte>> buffer,
                CancellationToken cancellationToken)
            {
                Debug.Assert(BufferCount >= 1 && BufferCount <= 2, "'bufferCount' MUST ONLY BE '1' or '2'.");

                bool sessionHandleLockTaken = false;
                AsyncOperationCompleted = false;
                ReceiveResult = null;
                try
                {
                    Monitor.Enter(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                    _webSocket.ThrowIfPendingException();
                    Initialize(buffer, cancellationToken);

                    while (ShouldContinue(cancellationToken))
                    {
                        WebSocketProtocolComponent.Action action;
                        WebSocketProtocolComponent.BufferType bufferType;

                        bool completed = false;

                        while (!completed)
                        {
                            Interop.WebSocket.Buffer[] dataBuffers =
                                new Interop.WebSocket.Buffer[BufferCount];
                            uint dataBufferCount = (uint)BufferCount;
                            IntPtr actionContext;

                            _webSocket.ThrowIfDisposed();
                            WebSocketProtocolComponent.WebSocketGetAction(_webSocket,
                                ActionQueue,
                                dataBuffers,
                                ref dataBufferCount,
                                out action,
                                out bufferType,
                                out actionContext);

                            switch (action)
                            {
                                case WebSocketProtocolComponent.Action.NoAction:
                                    if (ProcessAction_NoAction())
                                    {
                                        // A close frame was received

                                        Debug.Assert(ReceiveResult.Count == 0, "'receiveResult.Count' MUST be 0.");
                                        Debug.Assert(ReceiveResult.CloseStatus != null, "'receiveResult.CloseStatus' MUST NOT be NULL for message type 'Close'.");
                                        bool thisLockTaken = false;
                                        try
                                        {
                                            if (_webSocket.StartOnCloseReceived(ref thisLockTaken))
                                            {
                                                // If StartOnCloseReceived returns true the WebSocket close handshake has been completed
                                                // so there is no need to retake the SessionHandle-lock.
                                                // _ThisLock lock is guaranteed to be taken by StartOnCloseReceived when returning true
                                                ReleaseLock(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                                                bool callCompleteOnCloseCompleted = false;

                                                try
                                                {
                                                    callCompleteOnCloseCompleted = await _webSocket.StartOnCloseCompleted(
                                                        thisLockTaken, sessionHandleLockTaken, cancellationToken).SuppressContextFlow();
                                                }
                                                catch (Exception)
                                                {
                                                    // If an exception is thrown we know that the locks have been released,
                                                    // because we enforce IWebSocketStream.CloseNetworkConnectionAsync to yield
                                                    _webSocket.ResetFlagAndTakeLock(_webSocket._thisLock, ref thisLockTaken);
                                                    throw;
                                                }

                                                if (callCompleteOnCloseCompleted)
                                                {
                                                    _webSocket.ResetFlagAndTakeLock(_webSocket._thisLock, ref thisLockTaken);
                                                    _webSocket.FinishOnCloseCompleted();
                                                }
                                            }
                                            _webSocket.FinishOnCloseReceived(ReceiveResult.CloseStatus.Value, ReceiveResult.CloseStatusDescription);
                                        }
                                        finally
                                        {
                                            if (thisLockTaken)
                                            {
                                                ReleaseLock(_webSocket._thisLock, ref thisLockTaken);
                                            }
                                        }
                                    }
                                    completed = true;
                                    break;
                                case WebSocketProtocolComponent.Action.IndicateReceiveComplete:
                                    ProcessAction_IndicateReceiveComplete(buffer,
                                        bufferType,
                                        action,
                                        dataBuffers,
                                        dataBufferCount,
                                        actionContext);
                                    break;
                                case WebSocketProtocolComponent.Action.ReceiveFromNetwork:
                                    int count = 0;
                                    try
                                    {
                                        ArraySegment<byte> payload = _webSocket._internalBuffer.ConvertNativeBuffer(action, dataBuffers[0], bufferType);

                                        ReleaseLock(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                                        WebSocketValidate.ThrowIfConnectionAborted(_webSocket._innerStream, true);
                                        try
                                        {
                                            Task<int> readTask = _webSocket._innerStream.ReadAsync(payload.Array,
                                                payload.Offset,
                                                payload.Count,
                                                cancellationToken);
                                            count = await readTask.SuppressContextFlow();
                                            _webSocket._keepAliveTracker.OnDataReceived();
                                        }
                                        catch (ObjectDisposedException objectDisposedException)
                                        {
                                            throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely, objectDisposedException);
                                        }
                                        catch (NotSupportedException notSupportedException)
                                        {
                                            throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely, notSupportedException);
                                        }
                                        Monitor.Enter(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                                        _webSocket.ThrowIfPendingException();
                                        // If the client unexpectedly closed the socket we throw an exception as we didn't get any close message
                                        if (count == 0)
                                        {
                                            throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely);
                                        }
                                    }
                                    finally
                                    {
                                        WebSocketProtocolComponent.WebSocketCompleteAction(_webSocket,
                                            actionContext,
                                            count);
                                    }
                                    break;
                                case WebSocketProtocolComponent.Action.IndicateSendComplete:
                                    WebSocketProtocolComponent.WebSocketCompleteAction(_webSocket, actionContext, 0);
                                    AsyncOperationCompleted = true;
                                    ReleaseLock(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                                    await _webSocket._innerStream.FlushAsync().SuppressContextFlow();
                                    Monitor.Enter(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                                    break;
                                case WebSocketProtocolComponent.Action.SendToNetwork:
                                    int bytesSent = 0;
                                    try
                                    {
                                        if (_webSocket.State != WebSocketState.CloseSent ||
                                            (bufferType != WebSocketProtocolComponent.BufferType.PingPong &&
                                            bufferType != WebSocketProtocolComponent.BufferType.UnsolicitedPong))
                                        {
                                            if (dataBufferCount == 0)
                                            {
                                                break;
                                            }

                                            List<ArraySegment<byte>> sendBuffers = new List<ArraySegment<byte>>((int)dataBufferCount);
                                            int sendBufferSize = 0;
                                            ArraySegment<byte> framingBuffer = _webSocket._internalBuffer.ConvertNativeBuffer(action, dataBuffers[0], bufferType);
                                            sendBuffers.Add(framingBuffer);
                                            sendBufferSize += framingBuffer.Count;

                                            // There can be at most 2 dataBuffers
                                            // - one for the framing header and one for the payload
                                            if (dataBufferCount == 2)
                                            {
                                                ArraySegment<byte> payload;

                                                // The second buffer might be from the pinned send payload buffer (1) or from the
                                                // internal native buffer (2).  In the case of a PONG response being generated, the buffer
                                                // would be from (2).  Even if the payload is from a WebSocketSend operation, the buffer
                                                // might be (1) only if no buffer copies were needed (in the case of no masking, for example).
                                                // Or it might be (2).  So, we need to check.
                                                if (_webSocket._internalBuffer.IsPinnedSendPayloadBuffer(dataBuffers[1], bufferType))
                                                {
                                                    payload = _webSocket._internalBuffer.ConvertPinnedSendPayloadFromNative(dataBuffers[1], bufferType);
                                                }
                                                else
                                                {
                                                    payload = _webSocket._internalBuffer.ConvertNativeBuffer(action, dataBuffers[1], bufferType);
                                                }

                                                sendBuffers.Add(payload);
                                                sendBufferSize += payload.Count;
                                            }

                                            ReleaseLock(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                                            WebSocketValidate.ThrowIfConnectionAborted(_webSocket._innerStream, false);
                                            await _webSocket.SendFrameAsync(sendBuffers, cancellationToken).SuppressContextFlow();
                                            Monitor.Enter(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                                            _webSocket.ThrowIfPendingException();
                                            bytesSent += sendBufferSize;
                                            _webSocket._keepAliveTracker.OnDataSent();
                                        }
                                    }
                                    finally
                                    {
                                        WebSocketProtocolComponent.WebSocketCompleteAction(_webSocket,
                                            actionContext,
                                            bytesSent);
                                    }

                                    break;
                                default:
                                    string assertMessage = string.Format(CultureInfo.InvariantCulture,
                                        "Invalid action '{0}' returned from WebSocketGetAction.",
                                        action);
                                    Debug.Assert(false, assertMessage);
                                    throw new InvalidOperationException();
                            }
                        }

                        // WebSocketGetAction has returned NO_ACTION. In general, WebSocketGetAction will return
                        // NO_ACTION if there is no work item available to process at the current moment. But
                        // there could be work items on the queue still.  Those work items can't be returned back
                        // until the current work item (being done by another thread) is complete.
                        //
                        // It's possible that another thread might be finishing up an async operation and needs
                        // to call WebSocketCompleteAction. Once that happens, calling WebSocketGetAction on this
                        // thread might return something else to do. This happens, for example, if the RECEIVE thread
                        // ends up having to begin sending out a PONG response (due to it receiving a PING) and the
                        // current SEND thread has posted a WebSocketSend but it can't be processed yet until the
                        // RECEIVE thread has finished sending out the PONG response.
                        // 
                        // So, we need to release the lock briefly to give the other thread a chance to finish
                        // processing.  We won't actually exit this outter loop and return from this async method
                        // until the caller's async operation has been fully completed.
                        ReleaseLock(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                        Monitor.Enter(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                    }
                }
                finally
                {
                    Cleanup();
                    ReleaseLock(_webSocket.SessionHandle, ref sessionHandleLockTaken);
                }

                return ReceiveResult;
            }