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;
            }
        }
        // Requires lock taken.
        private Interop.WinHttp.SafeWinHttpHandle InitializeWinHttp(ClientWebSocketOptions options)
        {
            Interop.WinHttp.SafeWinHttpHandle sessionHandle;
            sessionHandle = Interop.WinHttp.WinHttpOpen(
                IntPtr.Zero,
                Interop.WinHttp.WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                null,
                null,
                (int)Interop.WinHttp.WINHTTP_FLAG_ASYNC);

            ThrowOnInvalidHandle(sessionHandle);

            uint optionAssuredNonBlockingTrue = 1; // TRUE

            if (!Interop.WinHttp.WinHttpSetOption(
                    sessionHandle,
                    Interop.WinHttp.WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS,
                    ref optionAssuredNonBlockingTrue,
                    (uint)Marshal.SizeOf <uint>()))
            {
                WinHttpException.ThrowExceptionUsingLastError();
            }

            return(sessionHandle);
        }
        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 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 void AddRequestHeaders(Uri uri, ClientWebSocketOptions options)
        {
            var requestHeadersBuffer = new StringBuilder();

            // Manually add cookies.
            if (options.Cookies != null)
            {
                string cookieHeader = GetCookieHeader(uri, options.Cookies);
                if (!string.IsNullOrEmpty(cookieHeader))
                {
                    requestHeadersBuffer.AppendLine(cookieHeader);
                }
            }

            // Serialize general request headers.
            requestHeadersBuffer.AppendLine(options.RequestHeaders.ToString());

            var subProtocols = options.RequestedSubProtocols;

            if (subProtocols.Count > 0)
            {
                requestHeadersBuffer.AppendLine(string.Format("{0}: {1}", HeaderNameWebSocketProtocol,
                                                              string.Join(", ", subProtocols)));
            }

            // Add request headers to WinHTTP request handle.
            if (!Interop.WinHttp.WinHttpAddRequestHeaders(
                    _operation.RequestHandle,
                    requestHeadersBuffer,
                    (uint)requestHeadersBuffer.Length,
                    Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD))
            {
                WinHttpException.ThrowExceptionUsingLastError();
            }
        }
        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);
            }
        }
 private void ThrowOnInvalidHandle(Interop.WinHttp.SafeWinHttpHandle value)
 {
     if (value.IsInvalid)
     {
         Abort();
         throw new WebSocketException(
                   SR.net_webstatus_ConnectFailure,
                   WinHttpException.CreateExceptionUsingLastError());
     }
 }
        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);
        }
        private Task <bool> InternalReceiveWsUpgradeResponse()
        {
            // TODO (Issue 2507): Potential optimization: move this in WinHttpWebSocketCallback.
            lock (_operation.Lock)
            {
                ThrowOnInvalidConnectState();

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

                if (!Interop.WinHttp.WinHttpReceiveResponse(_operation.RequestHandle, IntPtr.Zero))
                {
                    WinHttpException.ThrowExceptionUsingLastError();
                }
            }

            return(_operation.TcsUpgrade.Task);
        }
        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);
        }
Beispiel #13
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());
        }
        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);
            }
        }
        private HttpStatusCode GetHttpStatusCode()
        {
            uint infoLevel  = Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE | Interop.WinHttp.WINHTTP_QUERY_FLAG_NUMBER;
            uint result     = 0;
            uint resultSize = sizeof(uint);

            if (!Interop.WinHttp.WinHttpQueryHeaders(
                    _operation.RequestHandle,
                    infoLevel,
                    Interop.WinHttp.WINHTTP_HEADER_NAME_BY_INDEX,
                    ref result,
                    ref resultSize,
                    IntPtr.Zero))
            {
                WinHttpException.ThrowExceptionUsingLastError();
            }

            return((HttpStatusCode)result);
        }
        private Task <bool> InternalSendWsUpgradeRequestAsync()
        {
            lock (_operation.Lock)
            {
                ThrowOnInvalidConnectState();
                if (!Interop.WinHttp.WinHttpSendRequest(
                        _operation.RequestHandle,
                        Interop.WinHttp.WINHTTP_NO_ADDITIONAL_HEADERS,
                        0,
                        IntPtr.Zero,
                        0,
                        0,
                        _operation.ToIntPtr()))
                {
                    WinHttpException.ThrowExceptionUsingLastError();
                }
            }

            return(_operation.TcsUpgrade.Task);
        }
Beispiel #17
0
        private void AddRequestHeaders(Uri uri, ClientWebSocketOptions options)
        {
            var requestHeadersBuffer = new StringBuilder();

            // Manually add cookies.
            if (options.Cookies != null)
            {
                AppendCookieHeaderLine(uri, options.Cookies, requestHeadersBuffer);
            }

            // Serialize general request headers.
            requestHeadersBuffer.AppendLine(options.RequestHeaders.ToString());

            using (List <string> .Enumerator e = options.RequestedSubProtocols.GetEnumerator())
            {
                if (e.MoveNext())
                {
                    requestHeadersBuffer.Append(HeaderNameWebSocketProtocol + ": ");
                    requestHeadersBuffer.Append(e.Current);

                    while (e.MoveNext())
                    {
                        requestHeadersBuffer.Append(", ");
                        requestHeadersBuffer.Append(e.Current);
                    }

                    requestHeadersBuffer.AppendLine();
                }
            }

            // Add request headers to WinHTTP request handle.
            if (!Interop.WinHttp.WinHttpAddRequestHeaders(
                    _operation.RequestHandle,
                    requestHeadersBuffer,
                    (uint)requestHeadersBuffer.Length,
                    Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD))
            {
                WinHttpException.ThrowExceptionUsingLastError();
            }
        }
        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);
        }
Beispiel #19
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);
        }
        public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            _operation.InterlockedCheckAndUpdateState(WebSocketState.Connecting, s_validConnectStates);

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

                    // Must grab lock until RequestHandle is populated, otherwise we risk resource leaks on Abort.
                    //
                    // TODO (Issue 2506): Alternatively, release the lock between WinHTTP operations and check, under lock, that the
                    // state is still valid to continue operation.
                    _operation.SessionHandle = InitializeWinHttp(options);

                    _operation.ConnectionHandle = Interop.WinHttp.WinHttpConnectWithCallback(
                        _operation.SessionHandle,
                        uri.IdnHost,
                        (ushort)uri.Port,
                        0);

                    ThrowOnInvalidHandle(_operation.ConnectionHandle);

                    bool secureConnection = uri.Scheme == UriScheme.Https || uri.Scheme == UriScheme.Wss;

                    _operation.RequestHandle = Interop.WinHttp.WinHttpOpenRequestWithCallback(
                        _operation.ConnectionHandle,
                        "GET",
                        uri.PathAndQuery,
                        null,
                        Interop.WinHttp.WINHTTP_NO_REFERER,
                        null,
                        secureConnection ? Interop.WinHttp.WINHTTP_FLAG_SECURE : 0);

                    ThrowOnInvalidHandle(_operation.RequestHandle);
                    _operation.IncrementHandlesOpenWithCallback();

                    if (!Interop.WinHttp.WinHttpSetOption(
                            _operation.RequestHandle,
                            Interop.WinHttp.WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET,
                            IntPtr.Zero,
                            0))
                    {
                        WinHttpException.ThrowExceptionUsingLastError();
                    }

                    // We need the address of the IntPtr to the GCHandle.
                    IntPtr context = _operation.ToIntPtr();
                    IntPtr contextAddress;
                    unsafe
                    {
                        contextAddress = (IntPtr)(void *)&context;
                    }

                    if (!Interop.WinHttp.WinHttpSetOption(
                            _operation.RequestHandle,
                            Interop.WinHttp.WINHTTP_OPTION_CONTEXT_VALUE,
                            contextAddress,
                            (uint)IntPtr.Size))
                    {
                        WinHttpException.ThrowExceptionUsingLastError();
                    }

                    const uint notificationFlags =
                        Interop.WinHttp.WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS |
                        Interop.WinHttp.WINHTTP_CALLBACK_FLAG_HANDLES |
                        Interop.WinHttp.WINHTTP_CALLBACK_FLAG_SECURE_FAILURE;

                    if (Interop.WinHttp.WinHttpSetStatusCallback(
                            _operation.RequestHandle,
                            WinHttpWebSocketCallback.s_StaticCallbackDelegate,
                            notificationFlags,
                            IntPtr.Zero) == (IntPtr)Interop.WinHttp.WINHTTP_INVALID_STATUS_CALLBACK)
                    {
                        WinHttpException.ThrowExceptionUsingLastError();
                    }

                    _operation.RequestHandle.AttachCallback();

                    // We need to pin the operation object at this point in time since the WinHTTP callback
                    // has been fully wired to the request handle and the operation object has been set as
                    // the context value of the callback. Any notifications from activity on the handle will
                    // result in the callback being called with the context value.
                    _operation.Pin();

                    AddRequestHeaders(uri, options);

                    _operation.TcsUpgrade = new TaskCompletionSource <bool>();
                }

                await InternalSendWsUpgradeRequestAsync().ConfigureAwait(false);

                await InternalReceiveWsUpgradeResponse().ConfigureAwait(false);

                lock (_operation.Lock)
                {
                    VerifyUpgradeResponse();

                    ThrowOnInvalidConnectState();

                    _operation.WebSocketHandle =
                        Interop.WinHttp.WinHttpWebSocketCompleteUpgrade(_operation.RequestHandle, IntPtr.Zero);

                    ThrowOnInvalidHandle(_operation.WebSocketHandle);
                    _operation.IncrementHandlesOpenWithCallback();

                    // We need the address of the IntPtr to the GCHandle.
                    IntPtr context = _operation.ToIntPtr();
                    IntPtr contextAddress;
                    unsafe
                    {
                        contextAddress = (IntPtr)(void *)&context;
                    }

                    if (!Interop.WinHttp.WinHttpSetOption(
                            _operation.WebSocketHandle,
                            Interop.WinHttp.WINHTTP_OPTION_CONTEXT_VALUE,
                            contextAddress,
                            (uint)IntPtr.Size))
                    {
                        WinHttpException.ThrowExceptionUsingLastError();
                    }

                    _operation.WebSocketHandle.AttachCallback();
                    _operation.UpdateState(WebSocketState.Open);

                    if (_operation.RequestHandle != null)
                    {
                        _operation.RequestHandle.Dispose();
                        // RequestHandle will be set to null in the callback.
                    }
                    _operation.TcsUpgrade = null;

                    ctr.Dispose();
                }
            }
        }
Beispiel #21
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);
            }
        }