예제 #1
0
        // Validate the response headers and return the sub-protocol.
        private string ValidateResponse(HttpWebRequest request, HttpWebResponse response)
        {
            // 101
            if (response.StatusCode != HttpStatusCode.SwitchingProtocols)
            {
                throw new WebSocketException(SR.GetString(SR.net_WebSockets_Connect101Expected,
                                                          (int)response.StatusCode));
            }

            // Upgrade: websocket
            string upgradeHeader = response.Headers[HttpKnownHeaderNames.Upgrade];

            if (!string.Equals(upgradeHeader, WebSocketHelpers.WebSocketUpgradeToken,
                               StringComparison.OrdinalIgnoreCase))
            {
                throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader,
                                                          HttpKnownHeaderNames.Upgrade, upgradeHeader));
            }

            // Connection: Upgrade
            string connectionHeader = response.Headers[HttpKnownHeaderNames.Connection];

            if (!string.Equals(connectionHeader, HttpKnownHeaderNames.Upgrade,
                               StringComparison.OrdinalIgnoreCase))
            {
                throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader,
                                                          HttpKnownHeaderNames.Connection, connectionHeader));
            }

            // Sec-WebSocket-Accept derived from request Sec-WebSocket-Key
            string websocketAcceptHeader = response.Headers[HttpKnownHeaderNames.SecWebSocketAccept];
            string expectedAcceptHeader  = WebSocketHelpers.GetSecWebSocketAcceptString(
                request.Headers[HttpKnownHeaderNames.SecWebSocketKey]);

            if (!string.Equals(websocketAcceptHeader, expectedAcceptHeader, StringComparison.OrdinalIgnoreCase))
            {
                throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader,
                                                          HttpKnownHeaderNames.SecWebSocketAccept, websocketAcceptHeader));
            }

            // Sec-WebSocket-Protocol matches one from request
            // A missing header is ok.  It's also ok if the client didn't specify any.
            string subProtocol = response.Headers[HttpKnownHeaderNames.SecWebSocketProtocol];

            if (!string.IsNullOrWhiteSpace(subProtocol) && options.RequestedSubProtocols.Count > 0)
            {
                bool foundMatch = false;
                foreach (string requestedSubProtocol in options.RequestedSubProtocols)
                {
                    if (string.Equals(requestedSubProtocol, subProtocol, StringComparison.OrdinalIgnoreCase))
                    {
                        foundMatch = true;
                        break;
                    }
                }
                if (!foundMatch)
                {
                    throw new WebSocketException(SR.GetString(SR.net_WebSockets_AcceptUnsupportedProtocol,
                                                              string.Join(", ", options.RequestedSubProtocols), subProtocol));
                }
            }

            return(string.IsNullOrWhiteSpace(subProtocol) ? null : subProtocol); // May be null or valid.
        }
예제 #2
0
        private async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken)
        {
            HttpWebResponse response = null;
            CancellationTokenRegistration connectCancellation = new CancellationTokenRegistration();

            // Any errors from here on out are fatal and this instance will be disposed.
            try
            {
                HttpWebRequest request = CreateAndConfigureRequest(uri);
                if (Logging.On)
                {
                    Logging.Associate(Logging.WebSockets, this, request);
                }

                connectCancellation = cancellationToken.Register(AbortRequest, request, false);

                response = await request.GetResponseAsync().SuppressContextFlow() as HttpWebResponse;

                Contract.Assert(response != null, "Not an HttpWebResponse");

                this.RequestId = response.Headers.Get("X-RequestId");

                if (Logging.On)
                {
                    Logging.Associate(Logging.WebSockets, this, response);
                }

                string subprotocol = ValidateResponse(request, response);

                innerWebSocket = WebSocket.CreateClientWebSocket(response.GetResponseStream(), subprotocol,
                                                                 options.ReceiveBufferSize, options.SendBufferSize, options.KeepAliveInterval, false,
                                                                 options.GetOrCreateBuffer());

                if (Logging.On)
                {
                    Logging.Associate(Logging.WebSockets, this, innerWebSocket);
                }

                // Change internal state to 'connected' to enable the other methods
                if (Interlocked.CompareExchange(ref state, connected, connecting) != connecting)
                {
                    // Aborted/Disposed during connect.
                    throw new ObjectDisposedException(GetType().FullName);
                }
            }
            catch (WebException ex)
            {
                ConnectExceptionCleanup(response);
                WebSocketException wex = new WebSocketException(SR.GetString(SR.net_webstatus_ConnectFailure), ex);
                if (Logging.On)
                {
                    Logging.Exception(Logging.WebSockets, this, "ConnectAsync", wex);
                }
                throw wex;
            }
            catch (Exception ex)
            {
                ConnectExceptionCleanup(response);
                if (Logging.On)
                {
                    Logging.Exception(Logging.WebSockets, this, "ConnectAsync", ex);
                }
                throw;
            }
            finally
            {
                // We successfully connected (or failed trying), disengage from this token.
                // Otherwise any timeout/cancellation would apply to the full session.
                // In the failure case we need to release the reference to HWR.
                connectCancellation.Dispose();
            }
        }