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();
            }
        }
        // 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);
        }
Beispiel #3
0
        public Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var ws = new BrowserWebSocket();

            WebSocket = ws;
            return(ws.ConnectAsync(uri, options.RequestedSubProtocols, cancellationToken));
        }
        public ClientWebSocket() 
        {
            if (Logging.On) Logging.Enter(Logging.WebSockets, this, ".ctor", null);

            if (!WebSocketProtocolComponent.IsSupported)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            state = created;
            options = new ClientWebSocketOptions();
            cts = new CancellationTokenSource();

            if (Logging.On) Logging.Exit(Logging.WebSockets, this, ".ctor", null);
        }
Beispiel #5
0
 public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     try
     {
         await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
     }
     catch (Exception ex)
     {
         WebErrorStatus status = RTWebSocketError.GetStatus(ex.HResult);
         var inner = new Exception(status.ToString(), ex);
         WebSocketException wex = new WebSocketException(SR.net_webstatus_ConnectFailure, inner);
         if (NetEventSource.IsEnabled) NetEventSource.Error(_webSocket, wex);
         throw wex;
     }
 }
Beispiel #6
0
        public ClientWebSocket()
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this);
            }
            WebSocketHandle.CheckPlatformSupport();

            _state   = (int)InternalState.Created;
            _options = new ClientWebSocketOptions();

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this);
            }
        }
 public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     try
     {
         await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
     }
     catch (Win32Exception ex)
     {
         WebSocketException wex = new WebSocketException(SR.net_webstatus_ConnectFailure, ex);
         if (Logging.On)
         {
             Logging.Exception(Logging.WebSockets, this, "ConnectAsync", wex);
         }
         throw wex;
     }
 }
 public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     try
     {
         await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
     }
     catch (Win32Exception ex)
     {
         WebSocketException wex = new WebSocketException(SR.net_webstatus_ConnectFailure, ex);
         if (NetEventSource.Log.IsEnabled())
         {
             NetEventSource.Exception(NetEventSource.ComponentType.WebSocket, this, "ConnectAsync", wex);
         }
         throw wex;
     }
 }
        public ClientWebSocket()
        {
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Enter(NetEventSource.ComponentType.WebSocket, this, ".ctor", null);
            }

            WebSocketHandle.CheckPlatformSupport();

            _state = (int)InternalState.Created;
            _options = new ClientWebSocketOptions();

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Exit(NetEventSource.ComponentType.WebSocket, this, ".ctor", null);
            }
        }
Beispiel #10
0
        public ClientWebSocket()
        {
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Enter(NetEventSource.ComponentType.WebSocket, this, ".ctor", null);
            }

            WebSocketHandle.CheckPlatformSupport();

            _state   = (int)InternalState.Created;
            _options = new ClientWebSocketOptions();

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Exit(NetEventSource.ComponentType.WebSocket, this, ".ctor", null);
            }
        }
Beispiel #11
0
        public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            try
            {
                cancellationToken.ThrowIfCancellationRequested();  // avoid allocating a WebSocket object if cancellation was requested before connect
                CancellationTokenSource?linkedCancellation;
                CancellationTokenSource externalAndAbortCancellation;
                if (cancellationToken.CanBeCanceled) // avoid allocating linked source if external token is not cancelable
                {
                    linkedCancellation =
                        externalAndAbortCancellation =
                            CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _abortSource.Token);
                }
                else
                {
                    linkedCancellation           = null;
                    externalAndAbortCancellation = _abortSource;
                }

                using (linkedCancellation)
                {
                    WebSocket = new BrowserWebSocket();
                    await((BrowserWebSocket)WebSocket).ConnectAsyncJavaScript(uri, externalAndAbortCancellation.Token, options.RequestedSubProtocols).ConfigureAwait(continueOnCapturedContext: true);
                    externalAndAbortCancellation.Token.ThrowIfCancellationRequested();
                }
            }
            catch (Exception exc)
            {
                if (_state < WebSocketState.Closed)
                {
                    _state = WebSocketState.Closed;
                }

                Abort();

                switch (exc)
                {
                case WebSocketException:
                case OperationCanceledException _ when cancellationToken.IsCancellationRequested:
                    throw;

                default:
                    throw new WebSocketException(SR.net_webstatus_ConnectFailure, exc);
                }
            }
        }
        public ClientWebSocket()
        {
            if (Logging.On)
            {
                Logging.Enter(Logging.WebSockets, this, ".ctor", null);
            }

            WebSocketHandle.CheckPlatformSupport();

            _state   = (int)InternalState.Created;
            _options = new ClientWebSocketOptions();
            _cts     = new CancellationTokenSource();

            if (Logging.On)
            {
                Logging.Exit(Logging.WebSockets, this, ".ctor", null);
            }
        }
Beispiel #13
0
        public ClientWebSocket()
        {
            if (Logging.On)
            {
                Logging.Enter(Logging.WebSockets, this, ".ctor", null);
            }

            CheckPlatformSupport();

            _state = (int)InternalState.Created;
            _options = new ClientWebSocketOptions();
            _cts = new CancellationTokenSource();

            if (Logging.On)
            {
                Logging.Exit(Logging.WebSockets, this, ".ctor", null);
            }
        }
Beispiel #14
0
        public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            InterlockedCheckAndUpdateState(WebSocketState.Connecting, s_validConnectStates);
            CheckValidState(s_validConnectingStates);

            _messageWebSocket = new MessageWebSocket();
            foreach (var header in options.RequestHeaders)
            {
                _messageWebSocket.SetRequestHeader((string)header, options.RequestHeaders[(string)header]);
            }

            string cookies = options.Cookies == null ? null : options.Cookies.GetCookieHeader(uri);

            if (!string.IsNullOrEmpty(cookies))
            {
                _messageWebSocket.SetRequestHeader(HeaderNameCookie, cookies);
            }

            var websocketControl = _messageWebSocket.Control;

            foreach (var subProtocol in options.RequestedSubProtocols)
            {
                websocketControl.SupportedProtocols.Add(subProtocol);
            }

            try
            {
                _receiveAsyncBufferTcs             = new TaskCompletionSource <ArraySegment <byte> >();
                _closeWebSocketReceiveResultTcs    = new TaskCompletionSource <WebSocketReceiveResult>();
                _messageWebSocket.MessageReceived += OnMessageReceived;
                _messageWebSocket.Closed          += OnCloseReceived;
                await _messageWebSocket.ConnectAsync(uri).AsTask(cancellationToken);

                _subProtocol   = _messageWebSocket.Information.Protocol;
                _messageWriter = new DataWriter(_messageWebSocket.OutputStream);
            }
            catch (Exception)
            {
                UpdateState(WebSocketState.Closed);
                throw;
            }

            UpdateState(WebSocketState.Open);
        }
        public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            try
            {
                await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                WebErrorStatus status = RTWebSocketError.GetStatus(ex.HResult);
                var inner = new Exception(status.ToString(), ex);
                WebSocketException wex = new WebSocketException(SR.net_webstatus_ConnectFailure, inner);
                if (Logging.On)
                {
                    Logging.Exception(Logging.WebSockets, this, "ConnectAsync", wex);
                }

                throw wex;
            }
        }
Beispiel #16
0
        public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            InterlockedCheckAndUpdateState(WebSocketState.Connecting, s_validConnectStates);
            CheckValidState(s_validConnectingStates);

            _messageWebSocket = new MessageWebSocket();
            foreach (var header in options.RequestHeaders)
            {
                _messageWebSocket.SetRequestHeader((string)header, options.RequestHeaders[(string)header]);
            }

            string cookies = options.Cookies == null ? null : options.Cookies.GetCookieHeader(uri);
            if (!string.IsNullOrEmpty(cookies))
            {
                _messageWebSocket.SetRequestHeader(HeaderNameCookie, cookies);
            }

            var websocketControl = _messageWebSocket.Control;
            foreach (var subProtocol in options.RequestedSubProtocols)
            {
                websocketControl.SupportedProtocols.Add(subProtocol);
            }

            try
            {
                _receiveAsyncBufferTcs = new TaskCompletionSource<ArraySegment<byte>>();
                _closeWebSocketReceiveResultTcs = new TaskCompletionSource<WebSocketReceiveResult>();
                _messageWebSocket.MessageReceived += OnMessageReceived;
                _messageWebSocket.Closed += OnCloseReceived;
                await _messageWebSocket.ConnectAsync(uri).AsTask(cancellationToken);
                _subProtocol = _messageWebSocket.Information.Protocol;
                _messageWriter = new DataWriter(_messageWebSocket.OutputStream);
            }
            catch (Exception)
            {
                UpdateState(WebSocketState.Closed);
                throw;
            }

            UpdateState(WebSocketState.Open);
        }
Beispiel #17
0
        // internal API /////////////////////////////////////////////////////////////
        internal WebSocket(Uri uri, WebSocketListener listener)
        {
            bool ignoreCase = true;

            this.uri      = uri;
            this.listener = listener;

            this.protocols = new ArrayList();

            if ((String.Compare("ws", uri.Scheme, ignoreCase) != 0) &&
                (String.Compare("wss", uri.Scheme, ignoreCase) != 0))
            {
                throw new Exception("URI scheme must be 'ws' or 'wss'");
            }

            clientWebSocket = new ClientWebSocket();
            options         = clientWebSocket.Options;

            // turn off pongs from client to server.
            options.KeepAliveInterval = new System.TimeSpan(0);
        }
Beispiel #18
0
        public ClientWebSocket()
        {
            if (Logging.On)
            {
                Logging.Enter(Logging.WebSockets, this, ".ctor", null);
            }

            if (!WebSocketProtocolComponent.IsSupported)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            state   = created;
            options = new ClientWebSocketOptions();
            cts     = new CancellationTokenSource();

            if (Logging.On)
            {
                Logging.Exit(Logging.WebSockets, this, ".ctor", null);
            }
        }
Beispiel #19
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();
            }
        }
        public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            try
            {
                await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                // TODO #4143: ** TFS BUILD IS BROKEN ** 
                // This doesn't compile right now due to missing types 'WebErrorStatus' and 'RTWebSocketError'
                // Commenting out for now to allow the build to resume.
                //WebErrorStatus status = RTWebSocketError.GetStatus(ex.HResult);
                //var inner = new Exception(status.ToString(), ex);
                var inner = ex;
                WebSocketException wex = new WebSocketException(SR.net_webstatus_ConnectFailure, inner);
                if (Logging.On)
                {
                    Logging.Exception(Logging.WebSockets, this, "ConnectAsync", wex);
                }

                throw wex;
            }
        }
 public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     try
     {
         await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
     }
     catch (Win32Exception ex)
     {
         WebSocketException wex = new WebSocketException(SR.net_webstatus_ConnectFailure, ex);
         if (Logging.On)
         {
             Logging.Exception(Logging.WebSockets, this, "ConnectAsync", wex);
         }
         throw wex;
     }
 }
 public FrameworkClientWebSocketOptions(System.Net.WebSockets.ClientWebSocketOptions options)
 {
     this.options = options;
 }
        public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            if (options.RemoteCertificateValidationCallback != null)
            {
                throw new PlatformNotSupportedException(SR.net_WebSockets_RemoteValidationCallbackNotSupported);
            }

            try
            {
                await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                WebErrorStatus     status = RTWebSocketError.GetStatus(ex.HResult);
                var                inner  = new Exception(status.ToString(), ex);
                WebSocketException wex    = new WebSocketException(WebSocketError.Faulted, SR.net_webstatus_ConnectFailure, inner);
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(_webSocket, wex);
                }
                throw wex;
            }
        }
Beispiel #24
0
 public ClientWebSocket()
 {
     options      = new ClientWebSocketOptions();
     state        = WebSocketState.None;
     headerBuffer = new byte[HeaderMaxLength];
 }
 /// <summary>Adds the necessary headers for the web socket request.</summary>
 /// <param name="request">The request to which the headers should be added.</param>
 /// <param name="secKey">The generated security key to send in the Sec-WebSocket-Key header.</param>
 /// <param name="options">The options controlling the request.</param>
 private static void AddWebSocketHeaders(HttpRequestMessage request, string secKey, ClientWebSocketOptions options)
 {
     request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
     request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.Upgrade, "websocket");
     request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.SecWebSocketVersion, "13");
     request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.SecWebSocketKey, secKey);
     if (options._requestedSubProtocols?.Count > 0)
     {
         request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.SecWebSocketProtocol, string.Join(", ", options.RequestedSubProtocols));
     }
 }
Beispiel #26
0
        public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            try
            {
                await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                WebErrorStatus     status = RTWebSocketError.GetStatus(ex.HResult);
                var                inner  = new Exception(status.ToString(), ex);
                WebSocketException wex    = new WebSocketException(SR.net_webstatus_ConnectFailure, inner);
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Exception(NetEventSource.ComponentType.WebSocket, this, "ConnectAsync", wex);
                }

                throw wex;
            }
        }
Beispiel #27
0
        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();
            }
        }
Beispiel #28
0
        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);

                    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();
                    }

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

                    _operation.RequestHandle.AttachCallback();

                    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);

                    // 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 #29
0
		public ClientWebSocket ()
		{
			options = new ClientWebSocketOptions ();
			state = WebSocketState.None;
			headerBuffer = new byte[HeaderMaxLength];
		}
Beispiel #30
0
        // 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;
        }
        /// <summary>Creates a byte[] containing the headers to send to the server.</summary>
        /// <param name="uri">The Uri of the server.</param>
        /// <param name="options">The options used to configure the websocket.</param>
        /// <param name="secKey">The generated security key to send in the Sec-WebSocket-Key header.</param>
        /// <returns>The byte[] containing the encoded headers ready to send to the network.</returns>
        private static byte[] BuildRequestHeader(Uri uri, ClientWebSocketOptions options, string secKey)
        {
            StringBuilder builder = t_cachedStringBuilder ?? (t_cachedStringBuilder = new StringBuilder());
            Debug.Assert(builder.Length == 0, $"Expected builder to be empty, got one of length {builder.Length}");
            try
            {
                builder.Append("GET ").Append(uri.PathAndQuery).Append(" HTTP/1.1\r\n");

                // Add all of the required headers
                builder.Append("Host: ").Append(uri.IdnHost).Append(":").Append(uri.Port).Append("\r\n");
                builder.Append("Connection: Upgrade\r\n");
                builder.Append("Upgrade: websocket\r\n");
                builder.Append("Sec-WebSocket-Version: 13\r\n");
                builder.Append("Sec-WebSocket-Key: ").Append(secKey).Append("\r\n");

                // Add all of the additionally requested headers
                foreach (string key in options.RequestHeaders.AllKeys)
                {
                    builder.Append(key).Append(": ").Append(options.RequestHeaders[key]).Append("\r\n");
                }

                // Add the optional subprotocols header
                if (options.RequestedSubProtocols.Count > 0)
                {
                    builder.Append(HttpKnownHeaderNames.SecWebSocketProtocol).Append(": ");
                    builder.Append(options.RequestedSubProtocols[0]);
                    for (int i = 1; i < options.RequestedSubProtocols.Count; i++)
                    {
                        builder.Append(", ").Append(options.RequestedSubProtocols[i]);
                    }
                    builder.Append("\r\n");
                }

                // Add an optional cookies header
                if (options.Cookies != null)
                {
                    string header = options.Cookies.GetCookieHeader(uri);
                    if (!string.IsNullOrWhiteSpace(header))
                    {
                        builder.Append(HttpKnownHeaderNames.Cookie).Append(": ").Append(header).Append("\r\n");
                    }
                }

                // End the headers
                builder.Append("\r\n");

                // Return the bytes for the built up header
                return s_defaultHttpEncoding.GetBytes(builder.ToString());
            }
            finally
            {
                // Make sure we clear the builder
                builder.Clear();
            }
        }
        /// <summary>Read and validate the connect response headers from the server.</summary>
        /// <param name="stream">The stream from which to read the response headers.</param>
        /// <param name="options">The options used to configure the websocket.</param>
        /// <param name="expectedSecWebSocketAccept">The expected value of the Sec-WebSocket-Accept header.</param>
        /// <param name="cancellationToken">The CancellationToken to use to cancel the websocket.</param>
        /// <returns>The agreed upon subprotocol with the server, or null if there was none.</returns>
        private async Task<string> ParseAndValidateConnectResponseAsync(
            Stream stream, ClientWebSocketOptions options, string expectedSecWebSocketAccept, CancellationToken cancellationToken)
        {
            // Read the first line of the response
            string statusLine = await ReadResponseHeaderLineAsync(stream, cancellationToken).ConfigureAwait(false);

            // Depending on the underlying sockets implementation and timing, connecting to a server that then
            // immediately closes the connection may either result in an exception getting thrown from the connect
            // earlier, or it may result in getting to here but reading 0 bytes.  If we read 0 bytes and thus have
            // an empty status line, treat it as a connect failure.
            if (string.IsNullOrEmpty(statusLine))
            {
                throw new WebSocketException(SR.Format(SR.net_webstatus_ConnectFailure));
            }

            const string ExpectedStatusStart = "HTTP/1.1 ";
            const string ExpectedStatusStatWithCode = "HTTP/1.1 101"; // 101 == SwitchingProtocols

            // If the status line doesn't begin with "HTTP/1.1" or isn't long enough to contain a status code, fail.
            if (!statusLine.StartsWith(ExpectedStatusStart, StringComparison.Ordinal) || statusLine.Length < ExpectedStatusStatWithCode.Length)
            {
                throw new WebSocketException(WebSocketError.HeaderError);
            }

            // If the status line doesn't contain a status code 101, or if it's long enough to have a status description
            // but doesn't contain whitespace after the 101, fail.
            if (!statusLine.StartsWith(ExpectedStatusStatWithCode, StringComparison.Ordinal) ||
                (statusLine.Length > ExpectedStatusStatWithCode.Length && !char.IsWhiteSpace(statusLine[ExpectedStatusStatWithCode.Length])))
            {
                throw new WebSocketException(SR.net_webstatus_ConnectFailure);
            }

            // Read each response header. Be liberal in parsing the response header, treating
            // everything to the left of the colon as the key and everything to the right as the value, trimming both.
            // For each header, validate that we got the expected value.
            bool foundUpgrade = false, foundConnection = false, foundSecWebSocketAccept = false;
            string subprotocol = null;
            string line;
            while (!string.IsNullOrEmpty(line = await ReadResponseHeaderLineAsync(stream, cancellationToken).ConfigureAwait(false)))
            {
                int colonIndex = line.IndexOf(':');
                if (colonIndex == -1)
                {
                    throw new WebSocketException(WebSocketError.HeaderError);
                }

                string headerName = line.SubstringTrim(0, colonIndex);
                string headerValue = line.SubstringTrim(colonIndex + 1);

                // The Connection, Upgrade, and SecWebSocketAccept headers are required and with specific values.
                ValidateAndTrackHeader(HttpKnownHeaderNames.Connection, "Upgrade", headerName, headerValue, ref foundConnection);
                ValidateAndTrackHeader(HttpKnownHeaderNames.Upgrade, "websocket", headerName, headerValue, ref foundUpgrade);
                ValidateAndTrackHeader(HttpKnownHeaderNames.SecWebSocketAccept, expectedSecWebSocketAccept, headerName, headerValue, ref foundSecWebSocketAccept);

                // The SecWebSocketProtocol header is optional.  We should only get it with a non-empty value if we requested subprotocols,
                // and then it must only be one of the ones we requested.  If we got a subprotocol other than one we requested (or if we
                // already got one in a previous header), fail. Otherwise, track which one we got.
                if (string.Equals(HttpKnownHeaderNames.SecWebSocketProtocol, headerName, StringComparison.OrdinalIgnoreCase) &&
                    !string.IsNullOrWhiteSpace(headerValue))
                {
                    string newSubprotocol = options.RequestedSubProtocols.Find(requested => string.Equals(requested, headerValue, StringComparison.OrdinalIgnoreCase));
                    if (newSubprotocol == null || subprotocol != null)
                    {
                        throw new WebSocketException(
                            WebSocketError.UnsupportedProtocol,
                            SR.Format(SR.net_WebSockets_AcceptUnsupportedProtocol, string.Join(", ", options.RequestedSubProtocols), subprotocol));
                    }
                    subprotocol = newSubprotocol;
                }
            }
            if (!foundUpgrade || !foundConnection || !foundSecWebSocketAccept)
            {
                throw new WebSocketException(SR.net_webstatus_ConnectFailure);
            }

            return subprotocol;
        }
            public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
            {
                // Not currently used:
                // - ClientWebSocketOptions.Credentials
                // - ClientWebSocketOptions.Proxy

                lock (StateUpdateLock)
                {
                    ClientWebSocket.ThrowIfInvalidState(_state, _disposed, s_validConnectStates);
                    _state = WebSocketState.Connecting;
                }

                // Establish connection to the server
                CancellationTokenRegistration registration = cancellationToken.Register(s => ((ManagedClientWebSocket)s).Abort(), this);
                try
                {
                    // Connect to the remote server
                    Socket connectedSocket = await ConnectSocketAsync(uri.Host, uri.Port, cancellationToken).ConfigureAwait(false);
                    SetStream(new AsyncEventArgsNetworkStream(connectedSocket));

                    // Upgrade to SSL if needed
                    if (uri.Scheme == UriScheme.Wss)
                    {
                        var sslStream = new SslStream(_stream);
                        await sslStream.AuthenticateAsClientAsync(
                            uri.Host,
                            options.ClientCertificates,
                            SecurityProtocol.AllowedSecurityProtocols,
                            checkCertificateRevocation: false).ConfigureAwait(false);
                        SetStream(sslStream);
                    }

                    // Create the security key and expected response, then build all of the request headers
                    KeyValuePair<string, string> secKeyAndSecWebSocketAccept = CreateSecKeyAndSecWebSocketAccept();
                    byte[] requestHeader = BuildRequestHeader(uri, options, secKeyAndSecWebSocketAccept.Key);

                    // Write out the header to the connection
                    await _stream.WriteAsync(requestHeader, 0, requestHeader.Length, cancellationToken).ConfigureAwait(false);

                    // Parse the response and store our state for the remainder of the connection
                    _subprotocol = await ParseAndValidateConnectResponseAsync(options, secKeyAndSecWebSocketAccept.Value, cancellationToken).ConfigureAwait(false);

                    lock (StateUpdateLock)
                    {
                        if (_state == WebSocketState.Connecting)
                        {
                            _state = WebSocketState.Open;
                        }
                    }

                    // Now that we're opened, initiate the keep alive timer to send periodic pings
                    if (options.KeepAliveInterval > TimeSpan.Zero)
                    {
                        _keepAliveTimer = new Timer(
                            s => ((ManagedClientWebSocket)s).SendKeepAliveFrameAsync(), this,
                            options.KeepAliveInterval, options.KeepAliveInterval);
                    }
                }
                catch (Exception exc)
                {
                    lock (StateUpdateLock)
                    {
                        if (_state < WebSocketState.Closed)
                        {
                            _state = WebSocketState.Closed;
                        }
                    }

                    Abort();

                    if (exc is WebSocketException)
                    {
                        throw;
                    }
                    throw new WebSocketException(SR.net_webstatus_ConnectFailure, exc);
                }
                finally
                {
                    registration.Dispose();
                }
            }
Beispiel #34
0
 public Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     throw new PlatformNotSupportedException(SR.net_WebSockets_UnsupportedPlatform);
 }
        public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            HttpResponseMessage?response = null;
            SocketsHttpHandler? handler  = null;
            bool disposeHandler          = true;

            try
            {
                var request = new HttpRequestMessage(HttpMethod.Get, uri);
                if (options._requestHeaders?.Count > 0) // use field to avoid lazily initializing the collection
                {
                    foreach (string key in options.RequestHeaders)
                    {
                        request.Headers.TryAddWithoutValidation(key, options.RequestHeaders[key]);
                    }
                }

                // Create the security key and expected response, then build all of the request headers
                KeyValuePair <string, string> secKeyAndSecWebSocketAccept = CreateSecKeyAndSecWebSocketAccept();
                AddWebSocketHeaders(request, secKeyAndSecWebSocketAccept.Key, options);

                // Create the handler for this request and populate it with all of the options.
                // Try to use a shared handler rather than creating a new one just for this request, if
                // the options are compatible.
                if (options.Credentials == null &&
                    !options.UseDefaultCredentials &&
                    options.Proxy == null &&
                    options.Cookies == null &&
                    options.RemoteCertificateValidationCallback == null &&
                    options._clientCertificates?.Count == 0)
                {
                    disposeHandler = false;
                    handler        = s_defaultHandler;
                    if (handler == null)
                    {
                        handler = new SocketsHttpHandler()
                        {
                            PooledConnectionLifetime = TimeSpan.Zero,
                            UseProxy   = false,
                            UseCookies = false,
                        };
                        if (Interlocked.CompareExchange(ref s_defaultHandler, handler, null) != null)
                        {
                            handler.Dispose();
                            handler = s_defaultHandler;
                        }
                    }
                }
                else
                {
                    handler = new SocketsHttpHandler();
                    handler.PooledConnectionLifetime = TimeSpan.Zero;
                    handler.CookieContainer          = options.Cookies;
                    handler.UseCookies = options.Cookies != null;
                    handler.SslOptions.RemoteCertificateValidationCallback = options.RemoteCertificateValidationCallback;

                    if (options.UseDefaultCredentials)
                    {
                        handler.Credentials = CredentialCache.DefaultCredentials;
                    }
                    else
                    {
                        handler.Credentials = options.Credentials;
                    }

                    if (options.Proxy == null)
                    {
                        handler.UseProxy = false;
                    }
                    else if (options.Proxy != DefaultWebProxy.Instance)
                    {
                        handler.Proxy = options.Proxy;
                    }

                    if (options._clientCertificates?.Count > 0) // use field to avoid lazily initializing the collection
                    {
                        Debug.Assert(handler.SslOptions.ClientCertificates == null);
                        handler.SslOptions.ClientCertificates = new X509Certificate2Collection();
                        handler.SslOptions.ClientCertificates.AddRange(options.ClientCertificates);
                    }
                }

                // Issue the request.  The response must be status code 101.
                CancellationTokenSource?linkedCancellation;
                CancellationTokenSource externalAndAbortCancellation;
                if (cancellationToken.CanBeCanceled) // avoid allocating linked source if external token is not cancelable
                {
                    linkedCancellation =
                        externalAndAbortCancellation =
                            CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _abortSource.Token);
                }
                else
                {
                    linkedCancellation           = null;
                    externalAndAbortCancellation = _abortSource;
                }

                using (linkedCancellation)
                {
                    response = await new HttpMessageInvoker(handler).SendAsync(request, externalAndAbortCancellation.Token).ConfigureAwait(false);
                    externalAndAbortCancellation.Token.ThrowIfCancellationRequested(); // poll in case sends/receives in request/response didn't observe cancellation
                }

                if (response.StatusCode != HttpStatusCode.SwitchingProtocols)
                {
                    throw new WebSocketException(WebSocketError.NotAWebSocket, SR.Format(SR.net_WebSockets_Connect101Expected, (int)response.StatusCode));
                }

                // The Connection, Upgrade, and SecWebSocketAccept headers are required and with specific values.
                ValidateHeader(response.Headers, HttpKnownHeaderNames.Connection, "Upgrade");
                ValidateHeader(response.Headers, HttpKnownHeaderNames.Upgrade, "websocket");
                ValidateHeader(response.Headers, HttpKnownHeaderNames.SecWebSocketAccept, secKeyAndSecWebSocketAccept.Value);

                // The SecWebSocketProtocol header is optional.  We should only get it with a non-empty value if we requested subprotocols,
                // and then it must only be one of the ones we requested.  If we got a subprotocol other than one we requested (or if we
                // already got one in a previous header), fail. Otherwise, track which one we got.
                string?subprotocol = null;
                if (response.Headers.TryGetValues(HttpKnownHeaderNames.SecWebSocketProtocol, out IEnumerable <string>?subprotocolEnumerableValues))
                {
                    Debug.Assert(subprotocolEnumerableValues is string[]);
                    string[] subprotocolArray = (string[])subprotocolEnumerableValues;
                    if (subprotocolArray.Length > 0 && !string.IsNullOrEmpty(subprotocolArray[0]))
                    {
                        subprotocol = options.RequestedSubProtocols.Find(requested => string.Equals(requested, subprotocolArray[0], StringComparison.OrdinalIgnoreCase));
                        if (subprotocol == null)
                        {
                            throw new WebSocketException(
                                      WebSocketError.UnsupportedProtocol,
                                      SR.Format(SR.net_WebSockets_AcceptUnsupportedProtocol, string.Join(", ", options.RequestedSubProtocols), string.Join(", ", subprotocolArray)));
                        }
                    }
                }

                if (response.Content is null)
                {
                    throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely);
                }

                // Get the response stream and wrap it in a web socket.
                Stream connectedStream = response.Content.ReadAsStream();
                Debug.Assert(connectedStream.CanWrite);
                Debug.Assert(connectedStream.CanRead);
                WebSocket = WebSocket.CreateFromStream(
                    connectedStream,
                    isServer: false,
                    subprotocol,
                    options.KeepAliveInterval);
            }
            catch (Exception exc)
            {
                if (_state < WebSocketState.Closed)
                {
                    _state = WebSocketState.Closed;
                }

                Abort();
                response?.Dispose();

                if (exc is WebSocketException ||
                    (exc is OperationCanceledException && cancellationToken.IsCancellationRequested))
                {
                    throw;
                }

                throw new WebSocketException(WebSocketError.Faulted, SR.net_webstatus_ConnectFailure, exc);
            }
            finally
            {
                // Disposing the handler will not affect any active stream wrapped in the WebSocket.
                if (disposeHandler)
                {
                    handler?.Dispose();
                }
            }
        }
 public Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options) =>
     _webSocket.ConnectAsync(uri, cancellationToken, options);
 public Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     // TODO: Implement this to connect and set WebSocket to a working instance.
     return(Task.FromException(new PlatformNotSupportedException()));
 }
Beispiel #38
0
        public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            try
            {
                await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                // TODO #4143: ** TFS BUILD IS BROKEN **
                // This doesn't compile right now due to missing types 'WebErrorStatus' and 'RTWebSocketError'
                // Commenting out for now to allow the build to resume.
                //WebErrorStatus status = RTWebSocketError.GetStatus(ex.HResult);
                //var inner = new Exception(status.ToString(), ex);
                var inner = ex;
                WebSocketException wex = new WebSocketException(SR.net_webstatus_ConnectFailure, inner);
                if (Logging.On)
                {
                    Logging.Exception(Logging.WebSockets, this, "ConnectAsync", wex);
                }

                throw wex;
            }
        }
Beispiel #39
0
 public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     try
     {
         await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
     }
     catch (Win32Exception ex)
     {
         WebSocketException wex = new WebSocketException(SR.net_webstatus_ConnectFailure, ex);
         if (NetEventSource.Log.IsEnabled())
         {
             NetEventSource.Exception(NetEventSource.ComponentType.WebSocket, this, "ConnectAsync", wex);
         }
         throw wex;
     }
 }
        private static SocketsHttpHandler SetupHandler(ClientWebSocketOptions options, out bool disposeHandler)
        {
            SocketsHttpHandler?handler;

            // Create the handler for this request and populate it with all of the options.
            // Try to use a shared handler rather than creating a new one just for this request, if
            // the options are compatible.
            if (options.Credentials == null &&
                !options.UseDefaultCredentials &&
                options.Proxy == null &&
                options.Cookies == null &&
                options.RemoteCertificateValidationCallback == null &&
                (options._clientCertificates?.Count ?? 0) == 0)
            {
                disposeHandler = false;
                handler        = s_defaultHandler;
                if (handler == null)
                {
                    handler = new SocketsHttpHandler()
                    {
                        PooledConnectionLifetime = TimeSpan.Zero,
                        UseProxy   = false,
                        UseCookies = false,
                    };
                    if (Interlocked.CompareExchange(ref s_defaultHandler, handler, null) != null)
                    {
                        handler.Dispose();
                        handler = s_defaultHandler;
                    }
                }
            }
            else
            {
                disposeHandler = true;
                handler        = new SocketsHttpHandler();
                handler.PooledConnectionLifetime = TimeSpan.Zero;
                handler.CookieContainer          = options.Cookies;
                handler.UseCookies = options.Cookies != null;
                handler.SslOptions.RemoteCertificateValidationCallback = options.RemoteCertificateValidationCallback;

                handler.Credentials = options.UseDefaultCredentials ?
                                      CredentialCache.DefaultCredentials :
                                      options.Credentials;

                if (options.Proxy == null)
                {
                    handler.UseProxy = false;
                }
                else if (options.Proxy != DefaultWebProxy.Instance)
                {
                    handler.Proxy = options.Proxy;
                }

                if (options._clientCertificates?.Count > 0) // use field to avoid lazily initializing the collection
                {
                    Debug.Assert(handler.SslOptions.ClientCertificates == null);
                    handler.SslOptions.ClientCertificates = new X509Certificate2Collection();
                    handler.SslOptions.ClientCertificates.AddRange(options.ClientCertificates);
                }
            }

            return(handler);
        }
        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();
                }
            }
        }
        public async Task ConnectAsync(Uri uri, HttpMessageInvoker?invoker, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            bool disposeHandler = false;

            invoker ??= new HttpMessageInvoker(SetupHandler(options, out disposeHandler));
            HttpResponseMessage?response = null;

            bool disposeResponse = false;

            bool tryDowngrade = false;

            try
            {
                while (true)
                {
                    try
                    {
                        HttpRequestMessage request;
                        if (!tryDowngrade && options.HttpVersion >= HttpVersion.Version20 ||
                            (options.HttpVersion == HttpVersion.Version11 && options.HttpVersionPolicy == HttpVersionPolicy.RequestVersionOrHigher))
                        {
                            if (options.HttpVersion > HttpVersion.Version20 && options.HttpVersionPolicy != HttpVersionPolicy.RequestVersionOrLower)
                            {
                                throw new WebSocketException(WebSocketError.UnsupportedProtocol);
                            }
                            request = new HttpRequestMessage(HttpMethod.Connect, uri)
                            {
                                Version = HttpVersion.Version20
                            };
                            tryDowngrade = true;
                        }
                        else if (tryDowngrade || options.HttpVersion == HttpVersion.Version11)
                        {
                            request = new HttpRequestMessage(HttpMethod.Get, uri)
                            {
                                Version = HttpVersion.Version11
                            };
                            tryDowngrade = false;
                        }
                        else
                        {
                            throw new WebSocketException(WebSocketError.UnsupportedProtocol);
                        }

                        if (options._requestHeaders?.Count > 0) // use field to avoid lazily initializing the collection
                        {
                            foreach (string key in options.RequestHeaders)
                            {
                                request.Headers.TryAddWithoutValidation(key, options.RequestHeaders[key]);
                            }
                        }

                        string?secValue = AddWebSocketHeaders(request, options);

                        // Issue the request.
                        CancellationTokenSource?linkedCancellation;
                        CancellationTokenSource externalAndAbortCancellation;
                        if (cancellationToken.CanBeCanceled) // avoid allocating linked source if external token is not cancelable
                        {
                            linkedCancellation =
                                externalAndAbortCancellation =
                                    CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _abortSource.Token);
                        }
                        else
                        {
                            linkedCancellation           = null;
                            externalAndAbortCancellation = _abortSource;
                        }

                        using (linkedCancellation)
                        {
                            response = await invoker.SendAsync(request, externalAndAbortCancellation.Token).ConfigureAwait(false);

                            externalAndAbortCancellation.Token.ThrowIfCancellationRequested(); // poll in case sends/receives in request/response didn't observe cancellation
                        }

                        ValidateResponse(response, secValue, options);
                        break;
                    }
                    catch (HttpRequestException ex) when
                        ((ex.Data.Contains("SETTINGS_ENABLE_CONNECT_PROTOCOL") || ex.Data.Contains("HTTP2_ENABLED")) &&
                        tryDowngrade &&
                        (options.HttpVersion == HttpVersion.Version11 || options.HttpVersionPolicy == HttpVersionPolicy.RequestVersionOrLower))
                    {
                    }
                }

                // The SecWebSocketProtocol header is optional.  We should only get it with a non-empty value if we requested subprotocols,
                // and then it must only be one of the ones we requested.  If we got a subprotocol other than one we requested (or if we
                // already got one in a previous header), fail. Otherwise, track which one we got.
                string?subprotocol = null;
                if (response.Headers.TryGetValues(HttpKnownHeaderNames.SecWebSocketProtocol, out IEnumerable <string>?subprotocolEnumerableValues))
                {
                    Debug.Assert(subprotocolEnumerableValues is string[]);
                    string[] subprotocolArray = (string[])subprotocolEnumerableValues;
                    if (subprotocolArray.Length > 0 && !string.IsNullOrEmpty(subprotocolArray[0]))
                    {
                        if (options._requestedSubProtocols is not null)
                        {
                            foreach (string requestedProtocol in options._requestedSubProtocols)
                            {
                                if (requestedProtocol.Equals(subprotocolArray[0], StringComparison.OrdinalIgnoreCase))
                                {
                                    subprotocol = requestedProtocol;
                                    break;
                                }
                            }
                        }

                        if (subprotocol == null)
                        {
                            throw new WebSocketException(
                                      WebSocketError.UnsupportedProtocol,
                                      SR.Format(SR.net_WebSockets_AcceptUnsupportedProtocol, string.Join(", ", options.RequestedSubProtocols), string.Join(", ", subprotocolArray)));
                        }
                    }
                }

                // Because deflate options are negotiated we need a new object
                WebSocketDeflateOptions?negotiatedDeflateOptions = null;

                if (options.DangerousDeflateOptions is not null && response.Headers.TryGetValues(HttpKnownHeaderNames.SecWebSocketExtensions, out IEnumerable <string>?extensions))
                {
                    foreach (ReadOnlySpan <char> extension in extensions)
                    {
                        if (extension.TrimStart().StartsWith(ClientWebSocketDeflateConstants.Extension))
                        {
                            negotiatedDeflateOptions = ParseDeflateOptions(extension, options.DangerousDeflateOptions);
                            break;
                        }
                    }
                }

                // Get the response stream and wrap it in a web socket.
                Stream connectedStream = response.Content.ReadAsStream();
                Debug.Assert(connectedStream.CanWrite);
                Debug.Assert(connectedStream.CanRead);
                WebSocket = WebSocket.CreateFromStream(connectedStream, new WebSocketCreationOptions
                {
                    IsServer                = false,
                    SubProtocol             = subprotocol,
                    KeepAliveInterval       = options.KeepAliveInterval,
                    DangerousDeflateOptions = negotiatedDeflateOptions
                });
                _negotiatedDeflateOptions = negotiatedDeflateOptions;
            }
            catch (Exception exc)
            {
                if (_state < WebSocketState.Closed)
                {
                    _state = WebSocketState.Closed;
                }

                Abort();
                disposeResponse = true;

                if (exc is WebSocketException ||
                    (exc is OperationCanceledException && cancellationToken.IsCancellationRequested))
                {
                    throw;
                }

                throw new WebSocketException(WebSocketError.Faulted, SR.net_webstatus_ConnectFailure, exc);
            }
            finally
            {
                if (response is not null)
                {
                    if (options.CollectHttpResponseDetails)
                    {
                        HttpStatusCode      = response.StatusCode;
                        HttpResponseHeaders = new HttpResponseHeadersReadOnlyCollection(response.Headers);
                    }

                    if (disposeResponse)
                    {
                        response.Dispose();
                    }
                }

                // Disposing the handler will not affect any active stream wrapped in the WebSocket.
                if (disposeHandler)
                {
                    invoker?.Dispose();
                }
            }
        }
        public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            // TODO: Not currently implemented:
            // - ClientWebSocketOptions.Credentials
            // - ClientWebSocketOptions.Proxy

            // Establish connection to the server
            CancellationTokenRegistration registration = cancellationToken.Register(s => ((WebSocketHandle)s).Abort(), this);
            try
            {
                // Connect to the remote server
                Socket connectedSocket = await ConnectSocketAsync(uri.Host, uri.Port, cancellationToken).ConfigureAwait(false);
                Stream stream = new AsyncEventArgsNetworkStream(connectedSocket);

                // Upgrade to SSL if needed
                if (uri.Scheme == UriScheme.Wss)
                {
                    var sslStream = new SslStream(stream);
                    await sslStream.AuthenticateAsClientAsync(
                        uri.Host,
                        options.ClientCertificates,
                        SecurityProtocol.AllowedSecurityProtocols,
                        checkCertificateRevocation: false).ConfigureAwait(false);
                    stream = sslStream;
                }

                // Create the security key and expected response, then build all of the request headers
                KeyValuePair<string, string> secKeyAndSecWebSocketAccept = CreateSecKeyAndSecWebSocketAccept();
                byte[] requestHeader = BuildRequestHeader(uri, options, secKeyAndSecWebSocketAccept.Key);

                // Write out the header to the connection
                await stream.WriteAsync(requestHeader, 0, requestHeader.Length, cancellationToken).ConfigureAwait(false);

                // Parse the response and store our state for the remainder of the connection
                string subprotocol = await ParseAndValidateConnectResponseAsync(stream, options, secKeyAndSecWebSocketAccept.Value, cancellationToken).ConfigureAwait(false);

                _webSocket = ManagedWebSocket.CreateFromConnectedStream(stream, false, subprotocol);

                // If a concurrent Abort or Dispose came in before we set _webSocket, make sure to update it appropriately
                if (_state == WebSocketState.Aborted)
                {
                    _webSocket.Abort();
                }
                else if (_state == WebSocketState.Closed)
                {
                    _webSocket.Dispose();
                }
            }
            catch (Exception exc)
            {
                if (_state < WebSocketState.Closed)
                {
                    _state = WebSocketState.Closed;
                }

                Abort();

                if (exc is WebSocketException)
                {
                    throw;
                }
                throw new WebSocketException(SR.net_webstatus_ConnectFailure, exc);
            }
            finally
            {
                registration.Dispose();
            }
        }
Beispiel #44
0
		public ClientWebSocket ()
		{
			options = new ClientWebSocketOptions ();
			state = WebSocketState.None;
		}
Beispiel #45
0
        public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
        {
            InterlockedCheckAndUpdateState(WebSocketState.Connecting, s_validConnectStates);
            CheckValidState(s_validConnectingStates);

            _messageWebSocket = new MessageWebSocket();
            foreach (var header in options.RequestHeaders)
            {
                _messageWebSocket.SetRequestHeader((string)header, options.RequestHeaders[(string)header]);
            }

            string cookies = options.Cookies == null ? null : options.Cookies.GetCookieHeader(uri);

            if (!string.IsNullOrEmpty(cookies))
            {
                _messageWebSocket.SetRequestHeader(HeaderNameCookie, cookies);
            }

            var websocketControl = _messageWebSocket.Control;

            foreach (var subProtocol in options.RequestedSubProtocols)
            {
                websocketControl.SupportedProtocols.Add(subProtocol);
            }

            if (options.ClientCertificates.Count > 0)
            {
                if (!MessageWebSocketClientCertificateSupported)
                {
                    throw new PlatformNotSupportedException(string.Format(CultureInfo.InvariantCulture,
                                                                          SR.net_WebSockets_UWPClientCertSupportRequiresWindows10GreaterThan1703));
                }

                X509Certificate2 dotNetClientCert = CertificateHelper.GetEligibleClientCertificate(options.ClientCertificates);
                if (dotNetClientCert != null)
                {
                    RTCertificate winRtClientCert = await CertificateHelper.ConvertDotNetClientCertToWinRtClientCertAsync(dotNetClientCert).ConfigureAwait(false);

                    if (winRtClientCert == null)
                    {
                        throw new PlatformNotSupportedException(string.Format(
                                                                    CultureInfo.InvariantCulture,
                                                                    SR.net_WebSockets_UWPClientCertSupportRequiresCertInPersonalCertificateStore));
                    }

                    websocketControl.ClientCertificate = winRtClientCert;
                }
            }

            // Try to opt into PartialMessage receive mode so that we can hand partial data back to the app as it arrives.
            // If the MessageWebSocketControl.ReceiveMode API surface is not available, the MessageWebSocket.MessageReceived
            // event will only get triggered when an entire WebSocket message has been received. This results in large memory
            // footprint and prevents "streaming" scenarios (e.g., WCF) from working properly.
            if (MessageWebSocketReceiveModeSupported)
            {
                // Always enable partial message receive mode if the WinRT API supports it.
                _messageWebSocket.Control.ReceiveMode = MessageWebSocketReceiveMode.PartialMessage;
            }

            try
            {
                _receiveAsyncBufferTcs             = new TaskCompletionSource <ArraySegment <byte> >();
                _closeWebSocketReceiveResultTcs    = new TaskCompletionSource <WebSocketReceiveResult>();
                _messageWebSocket.MessageReceived += OnMessageReceived;
                _messageWebSocket.Closed          += OnCloseReceived;
                await _messageWebSocket.ConnectAsync(uri).AsTask(cancellationToken).ConfigureAwait(false);

                _subProtocol   = _messageWebSocket.Information.Protocol;
                _messageWriter = new DataWriter(_messageWebSocket.OutputStream);
            }
            catch (Exception)
            {
                UpdateState(WebSocketState.Closed);
                throw;
            }

            UpdateState(WebSocketState.Open);
        }
Beispiel #46
0
 public Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     throw new PlatformNotSupportedException(SR.net_WebSockets_UnsupportedPlatform);
 }
        public ClientWebSocket()
        {
            if (NetEventSource.IsEnabled) NetEventSource.Enter(this);
            WebSocketHandle.CheckPlatformSupport();

            _state = (int)InternalState.Created;
            _options = new ClientWebSocketOptions();

            if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
        }
 public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
 {
     try
     {
         await _webSocket.ConnectAsync(uri, cancellationToken, options).ConfigureAwait(false);
     }
     catch (Win32Exception ex)
     {
         var wex = new WebSocketException(SR.net_webstatus_ConnectFailure, ex);
         if (NetEventSource.IsEnabled) NetEventSource.Error(_webSocket, wex);
         throw wex;
     }
 }