コード例 #1
0
        internal static void UnwrapWebSocketBuffer(WebSocketProtocolComponent.Buffer buffer,
                                                   WebSocketProtocolComponent.BufferType bufferType,
                                                   out IntPtr bufferData,
                                                   out uint bufferLength)
        {
            bufferData   = IntPtr.Zero;
            bufferLength = 0;

            switch (bufferType)
            {
            case WebSocketProtocolComponent.BufferType.Close:
                bufferData   = buffer.CloseStatus.ReasonData;
                bufferLength = buffer.CloseStatus.ReasonLength;
                break;

            case WebSocketProtocolComponent.BufferType.None:
            case WebSocketProtocolComponent.BufferType.BinaryFragment:
            case WebSocketProtocolComponent.BufferType.BinaryMessage:
            case WebSocketProtocolComponent.BufferType.UTF8Fragment:
            case WebSocketProtocolComponent.BufferType.UTF8Message:
            case WebSocketProtocolComponent.BufferType.PingPong:
            case WebSocketProtocolComponent.BufferType.UnsolicitedPong:
                bufferData   = buffer.Data.BufferData;
                bufferLength = buffer.Data.BufferLength;
                break;

            default:
                Contract.Assert(false,
                                string.Format(CultureInfo.InvariantCulture,
                                              "BufferType '{0}' is invalid/unknown.",
                                              bufferType));
                break;
            }
        }
コード例 #2
0
        internal ArraySegment <byte> ConvertNativeBuffer(WebSocketProtocolComponent.Action action,
                                                         WebSocketProtocolComponent.Buffer buffer,
                                                         WebSocketProtocolComponent.BufferType bufferType)
        {
            ThrowIfDisposed();

            IntPtr bufferData;
            uint   bufferLength;

            UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferLength);

            if (bufferData == IntPtr.Zero)
            {
                return(WebSocketHelpers.EmptyPayload);
            }

            if (this.IsNativeBuffer(bufferData, bufferLength))
            {
                return(new ArraySegment <byte>(m_InternalBuffer.Array,
                                               this.GetOffset(bufferData),
                                               (int)bufferLength));
            }

            Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer.");
            // Indicates a violation in the contract with native Websocket.dll and could indicate
            // memory corruption because the internal buffer is shared between managed and native code
            throw new AccessViolationException();
        }
コード例 #3
0
        // This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
        private bool IsPinnedSendPayloadBuffer(WebSocketProtocolComponent.Buffer buffer,
                                               WebSocketProtocolComponent.BufferType bufferType)
        {
            if (m_SendBufferState != SendBufferState.SendPayloadSpecified)
            {
                return(false);
            }

            IntPtr bufferData;
            uint   bufferSize;

            UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferSize);

            long nativeBufferStartAddress = bufferData.ToInt64();
            long nativeBufferEndAddress   = nativeBufferStartAddress + bufferSize;

            return(nativeBufferStartAddress >= m_PinnedSendBufferStartAddress &&
                   nativeBufferEndAddress >= m_PinnedSendBufferStartAddress &&
                   nativeBufferStartAddress <= m_PinnedSendBufferEndAddress &&
                   nativeBufferEndAddress <= m_PinnedSendBufferEndAddress);
        }
コード例 #4
0
        internal void ConvertCloseBuffer(WebSocketProtocolComponent.Action action,
                                         WebSocketProtocolComponent.Buffer buffer,
                                         out WebSocketCloseStatus closeStatus,
                                         out string reason)
        {
            ThrowIfDisposed();
            IntPtr bufferData;
            uint   bufferLength;

            closeStatus = (WebSocketCloseStatus)buffer.CloseStatus.CloseStatus;

            UnwrapWebSocketBuffer(buffer, WebSocketProtocolComponent.BufferType.Close, out bufferData, out bufferLength);

            if (bufferData == IntPtr.Zero)
            {
                reason = null;
            }
            else
            {
                ArraySegment <byte> reasonBlob;
                if (this.IsNativeBuffer(bufferData, bufferLength))
                {
                    reasonBlob = new ArraySegment <byte>(m_InternalBuffer.Array,
                                                         this.GetOffset(bufferData),
                                                         (int)bufferLength);
                }
                else
                {
                    Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer.");
                    // Indicates a violation in the contract with native Websocket.dll and could indicate
                    // memory corruption because the internal buffer is shared between managed and native code
                    throw new AccessViolationException();
                }

                // No need to wrap DecoderFallbackException for invalid UTF8 chacters, because
                // Encoding.UTF8 will not throw but replace invalid characters instead.
                reason = Encoding.UTF8.GetString(reasonBlob.Array, reasonBlob.Offset, reasonBlob.Count);
            }
        }
コード例 #5
0
        // This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
        internal ArraySegment <byte> ConvertPinnedSendPayloadFromNative(WebSocketProtocolComponent.Buffer buffer,
                                                                        WebSocketProtocolComponent.BufferType bufferType)
        {
            if (!IsPinnedSendPayloadBuffer(buffer, bufferType))
            {
                // Indicates a violation in the API contract that could indicate
                // memory corruption because the pinned sendbuffer is shared between managed and native code
                throw new AccessViolationException();
            }

            Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(m_PinnedSendBuffer.Array,
                                                                   m_PinnedSendBuffer.Offset).ToInt64() == m_PinnedSendBufferStartAddress,
                            "'m_PinnedSendBuffer.Array' MUST be pinned during the entire send operation.");

            IntPtr bufferData;
            uint   bufferSize;

            UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferSize);

            int internalOffset = (int)(bufferData.ToInt64() - m_PinnedSendBufferStartAddress);

            return(new ArraySegment <byte>(m_PinnedSendBuffer.Array, m_PinnedSendBuffer.Offset + internalOffset, (int)bufferSize));
        }
コード例 #6
0
        internal void ValidateNativeBuffers(WebSocketProtocolComponent.Action action,
                                            WebSocketProtocolComponent.BufferType bufferType,
                                            WebSocketProtocolComponent.Buffer[] dataBuffers,
                                            uint dataBufferCount)
        {
            Contract.Assert(dataBufferCount <= (uint)int.MaxValue,
                            "'dataBufferCount' MUST NOT be bigger than Int32.MaxValue.");
            Contract.Assert(dataBuffers != null, "'dataBuffers' MUST NOT be NULL.");

            ThrowIfDisposed();
            if (dataBufferCount > dataBuffers.Length)
            {
                Contract.Assert(false, "'dataBufferCount' MUST NOT be bigger than 'dataBuffers.Length'.");
                // Indicates a violation in the contract with native Websocket.dll and could indicate
                // memory corruption because the internal buffer is shared between managed and native code
                throw new AccessViolationException();
            }

            int  count          = dataBuffers.Length;
            bool isSendActivity = action == WebSocketProtocolComponent.Action.IndicateSendComplete ||
                                  action == WebSocketProtocolComponent.Action.SendToNetwork;

            if (isSendActivity)
            {
                count = (int)dataBufferCount;
            }

            bool nonZeroBufferFound = false;

            for (int i = 0; i < count; i++)
            {
                WebSocketProtocolComponent.Buffer dataBuffer = dataBuffers[i];

                IntPtr bufferData;
                uint   bufferLength;
                UnwrapWebSocketBuffer(dataBuffer, bufferType, out bufferData, out bufferLength);

                if (bufferData == IntPtr.Zero)
                {
                    continue;
                }

                nonZeroBufferFound = true;

                bool isPinnedSendPayloadBuffer = IsPinnedSendPayloadBuffer(dataBuffer, bufferType);

                if (bufferLength > GetMaxBufferSize())
                {
                    if (!isSendActivity || !isPinnedSendPayloadBuffer)
                    {
                        Contract.Assert(false,
                                        "'dataBuffer.BufferLength' MUST NOT be bigger than 'm_ReceiveBufferSize' and 'm_SendBufferSize'.");
                        // Indicates a violation in the contract with native Websocket.dll and could indicate
                        // memory corruption because the internal buffer is shared between managed and native code
                        throw new AccessViolationException();
                    }
                }

                if (!isPinnedSendPayloadBuffer && !IsNativeBuffer(bufferData, bufferLength))
                {
                    Contract.Assert(false,
                                    "WebSocketGetAction MUST return a pointer within the pinned internal buffer.");
                    // Indicates a violation in the contract with native Websocket.dll and could indicate
                    // memory corruption because the internal buffer is shared between managed and native code
                    throw new AccessViolationException();
                }
            }

            if (!nonZeroBufferFound &&
                action != WebSocketProtocolComponent.Action.NoAction &&
                action != WebSocketProtocolComponent.Action.IndicateReceiveComplete &&
                action != WebSocketProtocolComponent.Action.IndicateSendComplete)
            {
                Contract.Assert(false, "At least one 'dataBuffer.Buffer' MUST NOT be NULL.");
            }
        }
コード例 #7
0
ファイル: WebSocketBase.cs プロジェクト: REALTOBIZ/mono
                protected override Nullable<WebSocketProtocolComponent.Buffer> CreateBuffer(Nullable<ArraySegment<byte>> buffer)
                {
                    Contract.Assert(buffer == null, "'buffer' MUST BE NULL.");
                    m_WebSocket.ThrowIfDisposed();
                    m_WebSocket.ThrowIfPendingException();

                    if (CloseStatus == WebSocketCloseStatus.Empty)
                    {
                        return null;
                    }

                    WebSocketProtocolComponent.Buffer payloadBuffer = new WebSocketProtocolComponent.Buffer();
                    if (CloseReason != null)
                    {
                        byte[] blob = UTF8Encoding.UTF8.GetBytes(CloseReason);
                        Contract.Assert(blob.Length <= WebSocketHelpers.MaxControlFramePayloadLength,
                            "The close reason is too long.");
                        ArraySegment<byte> closeBuffer = new ArraySegment<byte>(blob, 0, Math.Min(WebSocketHelpers.MaxControlFramePayloadLength, blob.Length));
                        m_WebSocket.m_InternalBuffer.PinSendBuffer(closeBuffer, out m_BufferHasBeenPinned);
                        payloadBuffer.CloseStatus.ReasonData = m_WebSocket.m_InternalBuffer.ConvertPinnedSendPayloadToNative(closeBuffer);
                        payloadBuffer.CloseStatus.ReasonLength = (uint)closeBuffer.Count;
                    }

                    payloadBuffer.CloseStatus.CloseStatus = (ushort)CloseStatus;
                    return payloadBuffer;
                }
コード例 #8
0
ファイル: WebSocketBase.cs プロジェクト: REALTOBIZ/mono
                protected virtual Nullable<WebSocketProtocolComponent.Buffer> CreateBuffer(Nullable<ArraySegment<byte>> buffer)
                {
                    if (buffer == null)
                    {
                        return null;
                    }

                    WebSocketProtocolComponent.Buffer payloadBuffer;
                    payloadBuffer = new WebSocketProtocolComponent.Buffer();
                    m_WebSocket.m_InternalBuffer.PinSendBuffer(buffer.Value, out m_BufferHasBeenPinned);
                    payloadBuffer.Data.BufferData = m_WebSocket.m_InternalBuffer.ConvertPinnedSendPayloadToNative(buffer.Value);
                    payloadBuffer.Data.BufferLength = (uint)buffer.Value.Count;
                    return payloadBuffer;
                }
コード例 #9
0
ファイル: WebSocketBase.cs プロジェクト: REALTOBIZ/mono
            internal async Task<WebSocketReceiveResult> Process(Nullable<ArraySegment<byte>> buffer,
                CancellationToken cancellationToken)
            {
                Contract.Assert(BufferCount >= 1 && BufferCount <= 2, "'bufferCount' MUST ONLY BE '1' or '2'.");

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

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

                        bool completed = false;

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

                            m_WebSocket.ThrowIfDisposed();
                            WebSocketProtocolComponent.WebSocketGetAction(m_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

                                        Contract.Assert(ReceiveResult.Count == 0, "'receiveResult.Count' MUST be 0.");
                                        Contract.Assert(ReceiveResult.CloseStatus != null, "'receiveResult.CloseStatus' MUST NOT be NULL for message type 'Close'.");
                                        bool thisLockTaken = false;
                                        try
                                        {
                                            if (m_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.
                                                // m_ThisLock lock is guaranteed to be taken by StartOnCloseReceived when returning true
                                                ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                                bool callCompleteOnCloseCompleted = false;

                                                try
                                                {
                                                    callCompleteOnCloseCompleted = await m_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
                                                    m_WebSocket.ResetFlagAndTakeLock(m_WebSocket.m_ThisLock, ref thisLockTaken);
                                                    throw;
                                                }

                                                if (callCompleteOnCloseCompleted)
                                                {
                                                    m_WebSocket.ResetFlagAndTakeLock(m_WebSocket.m_ThisLock, ref thisLockTaken);
                                                    m_WebSocket.FinishOnCloseCompleted();
                                                }
                                            }
                                            m_WebSocket.FinishOnCloseReceived(ReceiveResult.CloseStatus.Value, ReceiveResult.CloseStatusDescription);
                                        }
                                        finally
                                        {
                                            if (thisLockTaken)
                                            {
                                                ReleaseLock(m_WebSocket.m_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 = m_WebSocket.m_InternalBuffer.ConvertNativeBuffer(action, dataBuffers[0], bufferType);

                                        ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                        WebSocketHelpers.ThrowIfConnectionAborted(m_WebSocket.m_InnerStream, true);
                                        try
                                        {
                                            Task<int> readTask = m_WebSocket.m_InnerStream.ReadAsync(payload.Array,
                                                payload.Offset,
                                                payload.Count,
                                                cancellationToken);
                                            count = await readTask.SuppressContextFlow();
                                            m_WebSocket.m_KeepAliveTracker.OnDataReceived();
                                        }
                                        catch (ObjectDisposedException objectDisposedException)
                                        {
                                            throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely, objectDisposedException);
                                        }
                                        catch (NotSupportedException notSupportedException)
                                        {
                                            throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely, notSupportedException);
                                        }
                                        Monitor.Enter(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                        m_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(m_WebSocket,
                                            actionContext,
                                            count);
                                    }
                                    break;
                                case WebSocketProtocolComponent.Action.IndicateSendComplete:
                                    WebSocketProtocolComponent.WebSocketCompleteAction(m_WebSocket, actionContext, 0);
                                    AsyncOperationCompleted = true;
                                    ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                    await m_WebSocket.m_InnerStream.FlushAsync().SuppressContextFlow();
                                    Monitor.Enter(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                    break;
                                case WebSocketProtocolComponent.Action.SendToNetwork:
                                    int bytesSent = 0;
                                    try
                                    {
                                        if (m_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 = m_WebSocket.m_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 (m_WebSocket.m_InternalBuffer.IsPinnedSendPayloadBuffer(dataBuffers[1], bufferType))
                                                {
                                                    payload = m_WebSocket.m_InternalBuffer.ConvertPinnedSendPayloadFromNative(dataBuffers[1], bufferType);
                                                }
                                                else
                                                {
                                                    payload = m_WebSocket.m_InternalBuffer.ConvertNativeBuffer(action, dataBuffers[1], bufferType);
                                                }

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

                                            ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                            WebSocketHelpers.ThrowIfConnectionAborted(m_WebSocket.m_InnerStream, false);
                                            await m_WebSocket.SendFrameAsync(sendBuffers, cancellationToken).SuppressContextFlow();
                                            Monitor.Enter(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                            m_WebSocket.ThrowIfPendingException();
                                            bytesSent += sendBufferSize;
                                            m_WebSocket.m_KeepAliveTracker.OnDataSent();
                                        }
                                    }
                                    finally
                                    {
                                        WebSocketProtocolComponent.WebSocketCompleteAction(m_WebSocket,
                                            actionContext,
                                            bytesSent);
                                    }

                                    break;
                                default:
                                    string assertMessage = string.Format(CultureInfo.InvariantCulture,
                                        "Invalid action '{0}' returned from WebSocketGetAction.",
                                        action);
                                    Contract.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(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                        Monitor.Enter(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                    }
                }
                finally
                {
                    Cleanup();
                    ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                }

                return ReceiveResult;
            }
コード例 #10
0
            internal async Task<WebSocketReceiveResult> Process(Nullable<ArraySegment<byte>> buffer,
                CancellationToken cancellationToken)
            {
                Contract.Assert(BufferCount >= 1 && BufferCount <= 2, "'bufferCount' MUST ONLY BE '1' or '2'.");

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

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

                        bool completed = false;
                        while (!completed)
                        {
                            WebSocketProtocolComponent.Buffer[] dataBuffers =
                                new WebSocketProtocolComponent.Buffer[BufferCount];
                            uint dataBufferCount = (uint)BufferCount;
                            IntPtr actionContext;

                            m_WebSocket.ThrowIfDisposed();
                            WebSocketProtocolComponent.WebSocketGetAction(m_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

                                        Contract.Assert(ReceiveResult.Count == 0, "'receiveResult.Count' MUST be 0.");
                                        Contract.Assert(ReceiveResult.CloseStatus != null, "'receiveResult.CloseStatus' MUST NOT be NULL for message type 'Close'.");
                                        bool thisLockTaken = false;
                                        try
                                        {
                                            if (m_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.
                                                // m_ThisLock lock is guaranteed to be taken by StartOnCloseReceived when returning true
                                                ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                                bool callCompleteOnCloseCompleted = false;

                                                try
                                                {
                                                    callCompleteOnCloseCompleted = await m_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
                                                    m_WebSocket.ResetFlagAndTakeLock(m_WebSocket.m_ThisLock, ref thisLockTaken);
                                                    throw;
                                                }

                                                if (callCompleteOnCloseCompleted)
                                                {
                                                    m_WebSocket.ResetFlagAndTakeLock(m_WebSocket.m_ThisLock, ref thisLockTaken);
                                                    m_WebSocket.FinishOnCloseCompleted();
                                                }
                                            }
                                            m_WebSocket.FinishOnCloseReceived(ReceiveResult.CloseStatus.Value, ReceiveResult.CloseStatusDescription);
                                        }
                                        finally
                                        {
                                            if (thisLockTaken)
                                            {
                                                ReleaseLock(m_WebSocket.m_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 = m_WebSocket.m_InternalBuffer.ConvertNativeBuffer(action, dataBuffers[0], bufferType);

                                        ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                        WebSocketHelpers.ThrowIfConnectionAborted(m_WebSocket.m_InnerStream, true);
                                        try
                                        {
                                            Task<int> readTask = m_WebSocket.m_InnerStream.ReadAsync(payload.Array,
                                                payload.Offset,
                                                payload.Count,
                                                cancellationToken);
                                            count = await readTask.SuppressContextFlow();
                                            m_WebSocket.m_KeepAliveTracker.OnDataReceived();
                                        }
                                        catch (ObjectDisposedException objectDisposedException)
                                        {
                                            throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely, objectDisposedException);
                                        }
                                        catch (NotSupportedException notSupportedException)
                                        {
                                            throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely, notSupportedException);
                                        }
                                        Monitor.Enter(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                        m_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(m_WebSocket,
                                            actionContext,
                                            count);
                                    }
                                    break;
                                case WebSocketProtocolComponent.Action.IndicateSendComplete:
                                    WebSocketProtocolComponent.WebSocketCompleteAction(m_WebSocket, actionContext, 0);
                                    ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                    await m_WebSocket.m_InnerStream.FlushAsync().SuppressContextFlow();
                                    Monitor.Enter(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                    break;
                                case WebSocketProtocolComponent.Action.SendToNetwork:
                                    int bytesSent = 0;
                                    try
                                    {
                                        if (m_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 = m_WebSocket.m_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 = m_WebSocket.m_InternalBuffer.ConvertPinnedSendPayloadFromNative(dataBuffers[1], bufferType);
                                                sendBuffers.Add(payload);
                                                sendBufferSize += payload.Count;
                                            }

                                            ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                            WebSocketHelpers.ThrowIfConnectionAborted(m_WebSocket.m_InnerStream, false);
                                            await m_WebSocket.SendFrameAsync(sendBuffers, cancellationToken).SuppressContextFlow();
                                            Monitor.Enter(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                                            m_WebSocket.ThrowIfPendingException();
                                            bytesSent += sendBufferSize;
                                            m_WebSocket.m_KeepAliveTracker.OnDataSent();
                                        }
                                    }
                                    finally
                                    {
                                        WebSocketProtocolComponent.WebSocketCompleteAction(m_WebSocket,
                                            actionContext,
                                            bytesSent);
                                    }

                                    break;
                                default:
                                    string assertMessage = string.Format(CultureInfo.InvariantCulture,
                                        "Invalid action '{0}' returned from WebSocketGetAction.",
                                        action);
                                    Contract.Assert(false, assertMessage);
                                    throw new InvalidOperationException();
                            }
                        }
                    }
                }
                finally
                {
                    Cleanup();
                    ReleaseLock(m_WebSocket.SessionHandle, ref sessionHandleLockTaken);
                }

                return ReceiveResult;
            }