public Task WriteFragmentAsync(ArraySegment <byte> buffer, bool isUtf8Encoded, bool isFinalFragment)
        {
            TaskCompletionSource <object> tcs = new TaskCompletionSource <object>();

            // The buffer will be read from asynchronously by unmanaged code, so we require that it remain pinned
            PinnedArraySegment <byte> pinnedBuffer = new PinnedArraySegment <byte>(buffer);

            // Callback will always be called (since it is responsible for cleanup), even if completed synchronously
            CompletionCallback callback = (hrError, cbIO, fUtf8Encoded, fFinalFragment, fClose) => {
                try {
                    ThrowExceptionForHR(hrError);
                    tcs.TrySetResult(null); // regular completion
                }
                catch (Exception ex) {
                    tcs.TrySetException(ex); // exceptional completion
                }
                finally {
                    // Always free the buffer to prevent a memory leak
                    pinnedBuffer.Dispose();
                }
            };
            IntPtr completionContext = GCUtil.RootObject(callback);

            // update perf counter with count of data written to wire
            _perfCounters.IncrementCounter(AppPerfCounter.REQUEST_BYTES_OUT_WEBSOCKETS, pinnedBuffer.Count);

            // Call the underlying implementation; WriteFragment should never throw an exception
            int  bytesSent = pinnedBuffer.Count;
            bool completionExpected;
            int  hr = _context.WriteFragment(
                pData: pinnedBuffer.Pointer,
                pcbSent: ref bytesSent,
                fAsync: true,
                fUtf8Encoded: isUtf8Encoded,
                fFinalFragment: isFinalFragment,
                pfnCompletion: _asyncThunkAddress,
                pvCompletionContext: completionContext,
                pfCompletionExpected: out completionExpected);

            if (!completionExpected)
            {
                // Completed synchronously or error; the thunk and callback together handle cleanup
                AsyncCallbackThunk(hr, completionContext, bytesSent, isUtf8Encoded, isFinalFragment, fClose: false);
            }

            return(tcs.Task);
        }
        public Task WriteFragmentAsync(ArraySegment<byte> buffer, bool isUtf8Encoded, bool isFinalFragment) {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

            // The buffer will be read from asynchronously by unmanaged code, so we require that it remain pinned
            PinnedArraySegment<byte> pinnedBuffer = new PinnedArraySegment<byte>(buffer);

            // Callback will always be called (since it is responsible for cleanup), even if completed synchronously
            CompletionCallback callback = (hrError, cbIO, fUtf8Encoded, fFinalFragment, fClose) => {
                try {
                    ThrowExceptionForHR(hrError);
                    tcs.TrySetResult(null); // regular completion
                }
                catch (Exception ex) {
                    tcs.TrySetException(ex); // exceptional completion
                }
                finally {
                    // Always free the buffer to prevent a memory leak
                    pinnedBuffer.Dispose();
                }
            };
            IntPtr completionContext = GCUtil.RootObject(callback);

            // update perf counter with count of data written to wire
            _perfCounters.IncrementCounter(AppPerfCounter.REQUEST_BYTES_OUT_WEBSOCKETS, pinnedBuffer.Count);

            // Call the underlying implementation; WriteFragment should never throw an exception
            int bytesSent = pinnedBuffer.Count;
            bool completionExpected;
            int hr = _context.WriteFragment(
                    pData: pinnedBuffer.Pointer,
                    pcbSent: ref bytesSent,
                    fAsync: true,
                    fUtf8Encoded: isUtf8Encoded,
                    fFinalFragment: isFinalFragment,
                    pfnCompletion: _asyncThunkAddress,
                    pvCompletionContext: completionContext,
                    pfCompletionExpected: out completionExpected);

            if (!completionExpected) {
                // Completed synchronously or error; the thunk and callback together handle cleanup
                AsyncCallbackThunk(hr, completionContext, bytesSent, isUtf8Encoded, isFinalFragment, fClose: false);
            }

            return tcs.Task;
        }
        public Task <WebSocketReceiveResult> ReadFragmentAsync(ArraySegment <byte> buffer)
        {
            TaskCompletionSource <WebSocketReceiveResult> tcs = new TaskCompletionSource <WebSocketReceiveResult>();

            // The buffer will be written to asynchronously by unmanaged code, so we require that it remain pinned
            PinnedArraySegment <byte> pinnedBuffer = new PinnedArraySegment <byte>(buffer);

            // Callback will always be called (since it is responsible for cleanup), even if completed synchronously
            CompletionCallback callback = (hrError, cbIO, fUtf8Encoded, fFinalFragment, fClose) => {
                try {
                    ThrowExceptionForHR(hrError);

                    WebSocketCloseStatus?closeStatus = null;
                    string closeStatusDescription    = null;
                    WebSocketMessageType messageType = (fUtf8Encoded) ? WebSocketMessageType.Text : WebSocketMessageType.Binary;

                    if (fClose)
                    {
                        // this is a CLOSE frame
                        messageType = WebSocketMessageType.Close;
                        WebSocketCloseStatus statusCode;
                        GetCloseStatus(out statusCode, out closeStatusDescription);
                        closeStatus = statusCode;
                    }
                    else
                    {
                        // this is a data frame, so update perf counter with count of data read from wire
                        _perfCounters.IncrementCounter(AppPerfCounter.REQUEST_BYTES_IN_WEBSOCKETS, cbIO);
                    }

                    tcs.TrySetResult(new WebSocketReceiveResult(
                                         count: cbIO,
                                         messageType: messageType,
                                         endOfMessage: fFinalFragment,
                                         closeStatus: closeStatus,
                                         closeStatusDescription: closeStatusDescription));
                }
                catch (Exception ex) {
                    tcs.TrySetException(ex); // exceptional completion
                }
                finally {
                    // Always free the buffer to prevent a memory leak
                    pinnedBuffer.Dispose();
                }
            };
            IntPtr completionContext = GCUtil.RootObject(callback);

            // Call the underlying implementation; ReadFragment should never throw an exception
            int  bytesRead = pinnedBuffer.Count;
            bool isUtf8Encoded;
            bool isFinalFragment;
            bool isConnectionClose;
            bool completionExpected;
            int  hr = _context.ReadFragment(
                pData: pinnedBuffer.Pointer,
                pcbData: ref bytesRead,
                fAsync: true,
                pfUtf8Encoded: out isUtf8Encoded,
                pfFinalFragment: out isFinalFragment,
                pfConnectionClose: out isConnectionClose,
                pfnCompletion: _asyncThunkAddress,
                pvCompletionContext: completionContext,
                pfCompletionExpected: out completionExpected);

            if (!completionExpected)
            {
                // Completed synchronously or error; the thunk and callback together handle cleanup
                AsyncCallbackThunk(hr, completionContext, bytesRead, isUtf8Encoded, isFinalFragment, isConnectionClose);
            }

            return(tcs.Task);
        }
        public Task<WebSocketReceiveResult> ReadFragmentAsync(ArraySegment<byte> buffer) {
            TaskCompletionSource<WebSocketReceiveResult> tcs = new TaskCompletionSource<WebSocketReceiveResult>();

            // The buffer will be written to asynchronously by unmanaged code, so we require that it remain pinned
            PinnedArraySegment<byte> pinnedBuffer = new PinnedArraySegment<byte>(buffer);

            // Callback will always be called (since it is responsible for cleanup), even if completed synchronously
            CompletionCallback callback = (hrError, cbIO, fUtf8Encoded, fFinalFragment, fClose) => {
                try {
                    ThrowExceptionForHR(hrError);

                    WebSocketCloseStatus? closeStatus = null;
                    string closeStatusDescription = null;
                    WebSocketMessageType messageType = (fUtf8Encoded) ? WebSocketMessageType.Text : WebSocketMessageType.Binary;

                    if (fClose) {
                        // this is a CLOSE frame
                        messageType = WebSocketMessageType.Close;
                        WebSocketCloseStatus statusCode;
                        GetCloseStatus(out statusCode, out closeStatusDescription);
                        closeStatus = statusCode;
                    }
                    else {
                        // this is a data frame, so update perf counter with count of data read from wire
                        _perfCounters.IncrementCounter(AppPerfCounter.REQUEST_BYTES_IN_WEBSOCKETS, cbIO);
                    }

                    tcs.TrySetResult(new WebSocketReceiveResult(
                        count: cbIO,
                        messageType: messageType,
                        endOfMessage: fFinalFragment,
                        closeStatus: closeStatus,
                        closeStatusDescription: closeStatusDescription));
                }
                catch (Exception ex) {
                    tcs.TrySetException(ex); // exceptional completion
                }
                finally {
                    // Always free the buffer to prevent a memory leak
                    pinnedBuffer.Dispose();
                }
            };
            IntPtr completionContext = GCUtil.RootObject(callback);

            // Call the underlying implementation; ReadFragment should never throw an exception
            int bytesRead = pinnedBuffer.Count;
            bool isUtf8Encoded;
            bool isFinalFragment;
            bool isConnectionClose;
            bool completionExpected;
            int hr = _context.ReadFragment(
                pData: pinnedBuffer.Pointer,
                pcbData: ref bytesRead,
                fAsync: true,
                pfUtf8Encoded: out isUtf8Encoded,
                pfFinalFragment: out isFinalFragment,
                pfConnectionClose: out isConnectionClose,
                pfnCompletion: _asyncThunkAddress,
                pvCompletionContext: completionContext,
                pfCompletionExpected: out completionExpected);

            if (!completionExpected) {
                // Completed synchronously or error; the thunk and callback together handle cleanup
                AsyncCallbackThunk(hr, completionContext, bytesRead, isUtf8Encoded, isFinalFragment, isConnectionClose);
            }

            return tcs.Task;
        }