예제 #1
0
        public override Task SendAsync(
            ArraySegment <byte> buffer,
            WebSocketMessageType messageType,
            bool endOfMessage,
            CancellationToken cancellationToken)
        {
            _operation.InterlockedCheckValidStates(s_validSendStates);

            using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken))
            {
                var bufferType = WebSocketMessageTypeAdapter.GetWinHttpMessageType(messageType, endOfMessage);

                _operation.PinSendBuffer(buffer);

                bool sendOperationAlreadyPending = false;
                if (_operation.PendingWriteOperation == false)
                {
                    lock (_operation.Lock)
                    {
                        _operation.CheckValidState(s_validSendStates);

                        if (_operation.PendingWriteOperation == false)
                        {
                            _operation.PendingWriteOperation = true;
                            _operation.TcsSend = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

                            uint ret = Interop.WinHttp.WinHttpWebSocketSend(
                                _operation.WebSocketHandle,
                                bufferType,
                                buffer.Count > 0 ? Marshal.UnsafeAddrOfPinnedArrayElement(buffer.Array, buffer.Offset) : IntPtr.Zero,
                                (uint)buffer.Count);

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

                if (sendOperationAlreadyPending)
                {
                    var exception = new InvalidOperationException(
                        SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, "SendAsync"));

                    _operation.TcsSend.TrySetException(exception);
                    Abort();
                }

                return(_operation.TcsSend.Task);
            }
        }
예제 #2
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);
            }
        }
        private static void OnWebSocketError(
            WinHttpWebSocketState state,
            Interop.WinHttp.WINHTTP_WEB_SOCKET_ASYNC_RESULT asyncResult)
        {
            Debug.Assert(state != null, "OnWebSocketError: state is null");

            var innerException = WinHttpException.CreateExceptionUsingError((int)(asyncResult.AsyncResult.dwError));

            if (asyncResult.AsyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED)
            {
                var exception = new WebSocketException(
                    WebSocketError.InvalidState,
                    SR.Format(
                        SR.net_WebSockets_InvalidState_ClosedOrAborted,
                        "System.Net.WebSockets.InternalClientWebSocket",
                        "Aborted"),
                    innerException);

                state.UpdateState(WebSocketState.Aborted);

                if (state.TcsReceive != null)
                {
                    state.TcsReceive.TrySetException(exception);
                }

                if (state.TcsSend != null)
                {
                    state.TcsSend.TrySetException(exception);
                }

                return;
            }

            switch (asyncResult.Operation)
            {
            case Interop.WinHttp.WINHTTP_WEB_SOCKET_OPERATION.WINHTTP_WEB_SOCKET_SEND_OPERATION:
                state.PendingWriteOperation = false;
                state.TcsSend.TrySetException(innerException);
                break;

            case Interop.WinHttp.WINHTTP_WEB_SOCKET_OPERATION.WINHTTP_WEB_SOCKET_RECEIVE_OPERATION:
                state.PendingReadOperation = false;
                state.TcsReceive.TrySetException(innerException);
                break;

            case Interop.WinHttp.WINHTTP_WEB_SOCKET_OPERATION.WINHTTP_WEB_SOCKET_CLOSE_OPERATION:
                state.TcsClose.TrySetException(innerException);
                break;

            case Interop.WinHttp.WINHTTP_WEB_SOCKET_OPERATION.WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION:
                state.TcsCloseOutput.TrySetException(innerException);
                break;

            default:
                Debug.Fail(
                    "OnWebSocketError: Operation (" + asyncResult.Operation + ") is not expected.",
                    "Error code: " + asyncResult.AsyncResult.dwError + " (" + innerException.Message + ")");
                break;
            }
        }
        private static void OnRequestError(
            WinHttpWebSocketState state,
            Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult)
        {
            Debug.Assert(state != null, "OnRequestError: state is null");

            var innerException = WinHttpException.CreateExceptionUsingError((int)asyncResult.dwError);

            switch ((uint)asyncResult.dwResult.ToInt32())
            {
            case Interop.WinHttp.API_SEND_REQUEST:
            case Interop.WinHttp.API_RECEIVE_RESPONSE:
            {
                var exception = new WebSocketException(SR.net_webstatus_ConnectFailure, innerException);
                state.UpdateState(WebSocketState.Closed);
                state.TcsUpgrade.TrySetException(exception);
            }
            break;

            default:
            {
                Debug.Fail(
                    "OnRequestError: Result (" + asyncResult.dwResult + ") is not expected.",
                    "Error code: " + asyncResult.dwError + " (" + innerException.Message + ")");
            }
            break;
            }
        }
예제 #5
0
        private Task <bool> InternalReceiveAsync(ArraySegment <byte> buffer)
        {
            bool receiveOperationAlreadyPending = false;

            if (_operation.PendingReadOperation == false)
            {
                lock (_operation.Lock)
                {
                    if (_operation.PendingReadOperation == false)
                    {
                        _operation.CheckValidState(s_validReceiveStates);

                        // Prevent continuations from running on the same thread as the callback to prevent re-entrance deadlocks
                        _operation.TcsReceive           = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
                        _operation.PendingReadOperation = true;

                        uint bytesRead = 0;
                        Interop.WinHttp.WINHTTP_WEB_SOCKET_BUFFER_TYPE winHttpBufferType = 0;

                        uint status = Interop.WinHttp.WinHttpWebSocketReceive(
                            _operation.WebSocketHandle,
                            Marshal.UnsafeAddrOfPinnedArrayElement(buffer.Array, buffer.Offset),
                            (uint)buffer.Count,
                            out bytesRead,                          // Unused in async mode: ignore.
                            out winHttpBufferType);                 // Unused in async mode: ignore.

                        if (Interop.WinHttp.ERROR_SUCCESS != status)
                        {
                            throw WinHttpException.CreateExceptionUsingError((int)status);
                        }
                    }
                    else
                    {
                        receiveOperationAlreadyPending = true;
                    }
                }
            }
            else
            {
                receiveOperationAlreadyPending = true;
            }

            if (receiveOperationAlreadyPending)
            {
                var exception = new InvalidOperationException(
                    SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, "ReceiveAsync"));
                _operation.TcsReceive.TrySetException(exception);

                Abort();
            }

            return(_operation.TcsReceive.Task);
        }
        private static void OnWebSocketSecureFailure(WinHttpWebSocketState state, uint flags)
        {
            Debug.Assert(state != null, "OnWebSocketSecureFailure: state is null");

            var innerException = WinHttpException.CreateExceptionUsingError((int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE);
            var exception      = new WebSocketException(WebSocketError.ConnectionClosedPrematurely, innerException);

            // TODO (Issue 2509): handle SSL related exceptions.
            state.UpdateState(WebSocketState.Aborted);

            // TODO (Issue 2509): Create exception from WINHTTP_CALLBACK_STATUS_SECURE_FAILURE flags.
            state.TcsUpgrade.TrySetException(exception);
        }
        private static void OnRequestSecureFailure(WinHttpWebSocketState state, uint flags)
        {
            Debug.Assert(state != null, "OnRequestSecureFailure: state is null");

            var innerException = WinHttpException.CreateExceptionUsingError((int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE);

            var exception = new WebSocketException(
                WebSocketError.Success,
                SR.net_webstatus_ConnectFailure,
                innerException);

            // TODO: handle SSL related exceptions.
            state.UpdateState(WebSocketState.Closed);

            // TODO: Create exception from WINHTTP_CALLBACK_STATUS_SECURE_FAILURE flags.
            state.TcsUpgrade.TrySetException(exception);
        }
예제 #8
0
        private unsafe string GetResponseHeader(string headerName, char[] buffer = null)
        {
            const int StackLimit = 128;

            Debug.Assert(buffer == null || (buffer != null && buffer.Length > StackLimit));

            int bufferLength;

            if (buffer == null)
            {
                bufferLength = StackLimit;
                char *pBuffer = stackalloc char[bufferLength];
                if (QueryHeaders(headerName, pBuffer, ref bufferLength))
                {
                    return(new string(pBuffer, 0, bufferLength));
                }
            }
            else
            {
                bufferLength = buffer.Length;
                fixed(char *pBuffer = buffer)
                {
                    if (QueryHeaders(headerName, pBuffer, ref bufferLength))
                    {
                        return(new string(pBuffer, 0, bufferLength));
                    }
                }
            }

            int lastError = Marshal.GetLastWin32Error();

            if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND)
            {
                return(null);
            }

            if (lastError == Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER)
            {
                buffer = new char[bufferLength];
                return(GetResponseHeader(headerName, buffer));
            }

            throw WinHttpException.CreateExceptionUsingError(lastError);
        }
예제 #9
0
        public override Task CloseOutputAsync(
            WebSocketCloseStatus closeStatus,
            string statusDescription,
            CancellationToken cancellationToken)
        {
            _operation.InterlockedCheckAndUpdateState(WebSocketState.CloseSent, s_validCloseOutputStates);

            using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken))
            {
                lock (_operation.Lock)
                {
                    _operation.CheckValidState(s_validCloseOutputStatesAfterUpdate);

                    uint ret;
                    _operation.TcsCloseOutput = new TaskCompletionSource <bool>();

                    if (!string.IsNullOrEmpty(statusDescription))
                    {
                        byte[] statusDescriptionBuffer = Encoding.UTF8.GetBytes(statusDescription);

                        ret = Interop.WinHttp.WinHttpWebSocketShutdown(
                            _operation.WebSocketHandle,
                            (ushort)closeStatus,
                            statusDescriptionBuffer,
                            (uint)statusDescriptionBuffer.Length);
                    }
                    else
                    {
                        ret = Interop.WinHttp.WinHttpWebSocketShutdown(
                            _operation.WebSocketHandle,
                            (ushort)closeStatus,
                            IntPtr.Zero,
                            0);
                    }

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

                return(_operation.TcsCloseOutput.Task);
            }
        }
예제 #10
0
        private string GetResponseHeaderStringInfo(string headerName)
        {
            uint bytesNeeded = 0;
            bool results     = false;

            // Call WinHttpQueryHeaders once to obtain the size of the buffer needed.  The size is returned in
            // bytes but the API actually returns Unicode characters.
            if (!Interop.WinHttp.WinHttpQueryHeaders(
                    _operation.RequestHandle,
                    Interop.WinHttp.WINHTTP_QUERY_CUSTOM,
                    headerName,
                    null,
                    ref bytesNeeded,
                    IntPtr.Zero))
            {
                int lastError = Marshal.GetLastWin32Error();
                if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND)
                {
                    return(null);
                }

                if (lastError != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER)
                {
                    throw WinHttpException.CreateExceptionUsingError(lastError);
                }
            }

            int charsNeeded = (int)bytesNeeded / sizeof(char);
            var buffer      = new StringBuilder(charsNeeded, charsNeeded);

            results = Interop.WinHttp.WinHttpQueryHeaders(
                _operation.RequestHandle,
                Interop.WinHttp.WINHTTP_QUERY_CUSTOM,
                headerName,
                buffer,
                ref bytesNeeded,
                IntPtr.Zero);
            if (!results)
            {
                WinHttpException.ThrowExceptionUsingLastError();
            }

            return(buffer.ToString());
        }
예제 #11
0
        private Task <bool> InternalCloseAsync(WebSocketCloseStatus closeStatus, string statusDescription)
        {
            uint ret;

            _operation.TcsClose = new TaskCompletionSource <bool>();

            lock (_operation.Lock)
            {
                _operation.CheckValidState(s_validCloseStates);

                if (!string.IsNullOrEmpty(statusDescription))
                {
                    byte[] statusDescriptionBuffer = Encoding.UTF8.GetBytes(statusDescription);

                    ret = Interop.WinHttp.WinHttpWebSocketClose(
                        _operation.WebSocketHandle,
                        (ushort)closeStatus,
                        statusDescriptionBuffer,
                        (uint)statusDescriptionBuffer.Length);
                }
                else
                {
                    ret = Interop.WinHttp.WinHttpWebSocketClose(
                        _operation.WebSocketHandle,
                        (ushort)closeStatus,
                        IntPtr.Zero,
                        0);
                }

                if (ret != Interop.WinHttp.ERROR_SUCCESS)
                {
                    throw WinHttpException.CreateExceptionUsingError((int)ret);
                }
            }
            return(_operation.TcsClose.Task);
        }
예제 #12
0
        public override Task SendAsync(
            ArraySegment <byte> buffer,
            WebSocketMessageType messageType,
            bool endOfMessage,
            CancellationToken cancellationToken)
        {
            _operation.InterlockedCheckValidStates(s_validSendStates);

            using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken))
            {
                var bufferType = WebSocketMessageTypeAdapter.GetWinHttpMessageType(messageType, endOfMessage);

                // TODO (Issue 2505): replace with PinnableBufferCache.
                if (!_cachedSendPinnedBuffer.IsAllocated || _cachedSendPinnedBuffer.Target != buffer.Array)
                {
                    if (_cachedSendPinnedBuffer.IsAllocated)
                    {
                        _cachedSendPinnedBuffer.Free();
                    }

                    _cachedSendPinnedBuffer = GCHandle.Alloc(buffer.Array, GCHandleType.Pinned);
                }

                bool sendOperationAlreadyPending = false;
                if (_operation.PendingWriteOperation == false)
                {
                    lock (_operation.Lock)
                    {
                        _operation.CheckValidState(s_validSendStates);

                        if (_operation.PendingWriteOperation == false)
                        {
                            _operation.PendingWriteOperation = true;
                            _operation.TcsSend = new TaskCompletionSource <bool>();

                            uint ret = Interop.WinHttp.WinHttpWebSocketSend(
                                _operation.WebSocketHandle,
                                bufferType,
                                buffer.Count > 0 ? Marshal.UnsafeAddrOfPinnedArrayElement(buffer.Array, buffer.Offset) : IntPtr.Zero,
                                (uint)buffer.Count);

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

                if (sendOperationAlreadyPending)
                {
                    var exception = new InvalidOperationException(
                        SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, "SendAsync"));

                    _operation.TcsSend.TrySetException(exception);
                    Abort();
                }

                return(_operation.TcsSend.Task);
            }
        }
예제 #13
0
        private Task <bool> InternalReceiveAsync(Memory <byte> pinnedBuffer)
        {
            bool receiveOperationAlreadyPending = false;

            if (_operation.PendingReadOperation == false)
            {
                lock (_operation.Lock)
                {
                    if (_operation.PendingReadOperation == false)
                    {
                        _operation.CheckValidState(s_validReceiveStates);

                        // Prevent continuations from running on the same thread as the callback to prevent re-entrance deadlocks
                        _operation.TcsReceive           = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
                        _operation.PendingReadOperation = true;

                        uint bytesRead = 0;
                        Interop.WinHttp.WINHTTP_WEB_SOCKET_BUFFER_TYPE winHttpBufferType = 0;
                        IntPtr pinnedBufferPtr;
                        unsafe
                        {
                            fixed(byte *p = &pinnedBuffer.Span.DangerousGetPinnableReference())
                            {
                                pinnedBufferPtr = (IntPtr)p;
                            }
                        }

                        uint status = Interop.WinHttp.WinHttpWebSocketReceive(
                            _operation.WebSocketHandle,
                            pinnedBufferPtr,
                            (uint)pinnedBuffer.Length,
                            out bytesRead,                          // Unused in async mode: ignore.
                            out winHttpBufferType);                 // Unused in async mode: ignore.

                        if (Interop.WinHttp.ERROR_SUCCESS != status)
                        {
                            throw WinHttpException.CreateExceptionUsingError((int)status);
                        }
                    }
                    else
                    {
                        receiveOperationAlreadyPending = true;
                    }
                }
            }
            else
            {
                receiveOperationAlreadyPending = true;
            }

            if (receiveOperationAlreadyPending)
            {
                var exception = new InvalidOperationException(
                    SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, "ReceiveAsync"));
                _operation.TcsReceive.TrySetException(exception);

                Abort();
            }

            return(_operation.TcsReceive.Task);
        }