/// <summary>
        /// Starts the <see cref="Connection"/>.
        /// </summary>
        /// <param name="transport">The transport to use.</param>
        /// <returns>A task that represents when the connection has started.</returns>
        public Task Start(IClientTransport transport)
        {
            lock (_startLock)
            {
                _connectTask   = TaskAsyncHelper.Empty;
                _disconnectCts = new CancellationTokenSource();

                if (!ChangeState(ConnectionState.Disconnected, ConnectionState.Connecting))
                {
                    return(_connectTask);
                }

                _monitor   = new HeartbeatMonitor(this, _stateLock);
                _transport = transport;

                _connectTask = Negotiate(transport);
            }

            return(_connectTask);
        }
Пример #2
0
        private Task Negotiate(IClientTransport transport)
        {
            _connectionData = OnSending();

            return(transport.Negotiate(this, _connectionData)
                   .Then(negotiationResponse =>
            {
                VerifyProtocolVersion(negotiationResponse.ProtocolVersion);

                ConnectionId = negotiationResponse.ConnectionId;
                ConnectionToken = negotiationResponse.ConnectionToken;
                _disconnectTimeout = TimeSpan.FromSeconds(negotiationResponse.DisconnectTimeout);
                _totalTransportConnectTimeout = TransportConnectTimeout + TimeSpan.FromSeconds(negotiationResponse.TransportConnectTimeout);

                // Default the beat interval to be 5 seconds in case keep alive is disabled.
                var beatInterval = TimeSpan.FromSeconds(5);

                // If we have a keep alive
                if (negotiationResponse.KeepAliveTimeout != null)
                {
                    _keepAliveData = new KeepAliveData(TimeSpan.FromSeconds(negotiationResponse.KeepAliveTimeout.Value));
                    _reconnectWindow = _disconnectTimeout + _keepAliveData.Timeout;

                    beatInterval = _keepAliveData.CheckInterval;
                }
                else
                {
                    _reconnectWindow = _disconnectTimeout;
                }

                _monitor = new HeartbeatMonitor(this, _stateLock, beatInterval);

                return StartTransport();
            })
                   .ContinueWithNotComplete(() => Disconnect()));
        }
Пример #3
0
        private Task Negotiate(IClientTransport transport)
        {
            // Captured and used by StartNegotiation to track attempts
            var negotiationAttempts = 0;

            Task CompleteNegotiation(NegotiationResponse negotiationResponse)
            {
                ConnectionId                  = negotiationResponse.ConnectionId;
                ConnectionToken               = negotiationResponse.ConnectionToken;
                _disconnectTimeout            = TimeSpan.FromSeconds(negotiationResponse.DisconnectTimeout);
                _totalTransportConnectTimeout = TransportConnectTimeout + TimeSpan.FromSeconds(negotiationResponse.TransportConnectTimeout);

                // Default the beat interval to be 5 seconds in case keep alive is disabled.
                var beatInterval = TimeSpan.FromSeconds(5);

                // If we have a keep alive
                if (negotiationResponse.KeepAliveTimeout != null)
                {
                    _keepAliveData   = new KeepAliveData(TimeSpan.FromSeconds(negotiationResponse.KeepAliveTimeout.Value));
                    _reconnectWindow = _disconnectTimeout + _keepAliveData.Timeout;

                    beatInterval = _keepAliveData.CheckInterval;
                }
                else
                {
                    _reconnectWindow = _disconnectTimeout;
                }

                Monitor = new HeartbeatMonitor(this, _stateLock, beatInterval);

                return(StartTransport());
            }

            Task StartNegotiation()
            {
                return(transport.Negotiate(this, _connectionData)
                       .Then(negotiationResponse =>
                {
                    VerifyProtocolVersion(negotiationResponse.ProtocolVersion);

                    if (negotiationResponse.ProtocolVersion.Equals("2.0"))
                    {
                        if (!string.IsNullOrEmpty(negotiationResponse.Error))
                        {
                            throw new StartException(string.Format(Resources.Error_ErrorFromServer, negotiationResponse.Error));
                        }
                        if (!string.IsNullOrEmpty(negotiationResponse.RedirectUrl))
                        {
                            if (!negotiationResponse.RedirectUrl.EndsWith("/"))
                            {
                                negotiationResponse.RedirectUrl += "/";
                            }

                            // Update the URL based on the redirect response and restart the negotiation
                            Url = negotiationResponse.RedirectUrl;

                            if (!string.IsNullOrEmpty(negotiationResponse.AccessToken))
                            {
                                // This will stomp on the current Authorization header, but that's by design.
                                // If the server specified a token, that is expected to overrule the token the client is currently using.
                                Headers["Authorization"] = $"Bearer {negotiationResponse.AccessToken}";
                            }

                            negotiationAttempts += 1;
                            if (negotiationAttempts >= MaxRedirects)
                            {
                                throw new InvalidOperationException(Resources.Error_NegotiationLimitExceeded);
                            }
                            return StartNegotiation();
                        }
                    }

                    return CompleteNegotiation(negotiationResponse);
                })
                       .ContinueWithNotComplete(() => Disconnect()));
            }

            _connectionData = OnSending();

            return(StartNegotiation());
        }