| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "remoting/host/signaling_connector.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/callback.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "google_apis/google_api_keys.h" | 
 | #include "net/url_request/url_fetcher.h" | 
 | #include "net/url_request/url_request_context_getter.h" | 
 | #include "remoting/base/logging.h" | 
 | #include "remoting/host/dns_blackhole_checker.h" | 
 | #include "remoting/signaling/signaling_address.h" | 
 |  | 
 | namespace remoting { | 
 |  | 
 | namespace { | 
 |  | 
 | // The delay between reconnect attempts will increase exponentially up | 
 | // to the maximum specified here. | 
 | const int kMaxReconnectDelaySeconds = 10 * 60; | 
 |  | 
 | const char* SignalStrategyErrorToString(SignalStrategy::Error error){ | 
 |   switch(error) { | 
 |     case SignalStrategy::OK: | 
 |       return "OK"; | 
 |     case SignalStrategy::AUTHENTICATION_FAILED: | 
 |       return "AUTHENTICATION_FAILED"; | 
 |     case SignalStrategy::NETWORK_ERROR: | 
 |       return "NETWORK_ERROR"; | 
 |     case SignalStrategy::PROTOCOL_ERROR: | 
 |       return "PROTOCOL_ERROR"; | 
 |   } | 
 |   NOTREACHED(); | 
 |   return ""; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | SignalingConnector::SignalingConnector( | 
 |     XmppSignalStrategy* signal_strategy, | 
 |     std::unique_ptr<DnsBlackholeChecker> dns_blackhole_checker, | 
 |     OAuthTokenGetter* oauth_token_getter, | 
 |     const base::Closure& auth_failed_callback) | 
 |     : signal_strategy_(signal_strategy), | 
 |       auth_failed_callback_(auth_failed_callback), | 
 |       dns_blackhole_checker_(std::move(dns_blackhole_checker)), | 
 |       oauth_token_getter_(oauth_token_getter), | 
 |       reconnect_attempts_(0), | 
 |       weak_factory_(this) { | 
 |   DCHECK(!auth_failed_callback_.is_null()); | 
 |   DCHECK(dns_blackhole_checker_.get()); | 
 |   net::NetworkChangeNotifier::AddNetworkChangeObserver(this); | 
 |   signal_strategy_->AddListener(this); | 
 |   ScheduleTryReconnect(); | 
 | } | 
 |  | 
 | SignalingConnector::~SignalingConnector() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   signal_strategy_->RemoveListener(this); | 
 |   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); | 
 | } | 
 |  | 
 | void SignalingConnector::OnSignalStrategyStateChange( | 
 |     SignalStrategy::State state) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (state == SignalStrategy::CONNECTED) { | 
 |     HOST_LOG << "Signaling connected. New JID: " | 
 |              << signal_strategy_->GetLocalAddress().jid(); | 
 |     reconnect_attempts_ = 0; | 
 |   } else if (state == SignalStrategy::DISCONNECTED) { | 
 |     HOST_LOG << "Signaling disconnected. error=" | 
 |              << SignalStrategyErrorToString(signal_strategy_->GetError()); | 
 |     reconnect_attempts_++; | 
 |  | 
 |     if (signal_strategy_->GetError() == SignalStrategy::AUTHENTICATION_FAILED) | 
 |       oauth_token_getter_->InvalidateCache(); | 
 |  | 
 |     ScheduleTryReconnect(); | 
 |   } | 
 | } | 
 |  | 
 | bool SignalingConnector::OnSignalStrategyIncomingStanza( | 
 |     const buzz::XmlElement* stanza) { | 
 |   return false; | 
 | } | 
 |  | 
 | void SignalingConnector::OnNetworkChanged( | 
 |     net::NetworkChangeNotifier::ConnectionType type) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   if (type != net::NetworkChangeNotifier::CONNECTION_NONE && | 
 |       signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) { | 
 |     HOST_LOG << "Network state changed to online."; | 
 |     ResetAndTryReconnect(); | 
 |   } | 
 | } | 
 |  | 
 | void SignalingConnector::OnAccessToken(OAuthTokenGetter::Status status, | 
 |                                        const std::string& user_email, | 
 |                                        const std::string& access_token) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   if (status == OAuthTokenGetter::AUTH_ERROR) { | 
 |     auth_failed_callback_.Run(); | 
 |     return; | 
 |   } else if (status == OAuthTokenGetter::NETWORK_ERROR) { | 
 |     OnNetworkError(); | 
 |     return; | 
 |   } | 
 |  | 
 |   DCHECK_EQ(status, OAuthTokenGetter::SUCCESS); | 
 |   HOST_LOG << "Received user info."; | 
 |  | 
 |   signal_strategy_->SetAuthInfo(user_email, access_token); | 
 |  | 
 |   // Now that we've refreshed the token and verified that it's for the correct | 
 |   // user account, try to connect using the new token. | 
 |   DCHECK_EQ(signal_strategy_->GetState(), SignalStrategy::DISCONNECTED); | 
 |   signal_strategy_->Connect(); | 
 | } | 
 |  | 
 | void SignalingConnector::OnNetworkError() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   reconnect_attempts_++; | 
 |   ScheduleTryReconnect(); | 
 | } | 
 |  | 
 | void SignalingConnector::ScheduleTryReconnect() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   if (timer_.IsRunning() || net::NetworkChangeNotifier::IsOffline()) | 
 |     return; | 
 |   int delay_s = std::min(1 << reconnect_attempts_, | 
 |                          kMaxReconnectDelaySeconds); | 
 |   timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay_s), | 
 |                this, &SignalingConnector::TryReconnect); | 
 | } | 
 |  | 
 | void SignalingConnector::ResetAndTryReconnect() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   signal_strategy_->Disconnect(); | 
 |   reconnect_attempts_ = 0; | 
 |   timer_.Stop(); | 
 |   ScheduleTryReconnect(); | 
 | } | 
 |  | 
 | void SignalingConnector::TryReconnect() { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |   DCHECK(dns_blackhole_checker_.get()); | 
 |  | 
 |   // This will check if this machine is allowed to access the chromoting | 
 |   // host talkgadget. | 
 |   dns_blackhole_checker_->CheckForDnsBlackhole( | 
 |       base::Bind(&SignalingConnector::OnDnsBlackholeCheckerDone, | 
 |                  base::Unretained(this))); | 
 | } | 
 |  | 
 | void SignalingConnector::OnDnsBlackholeCheckerDone(bool allow) { | 
 |   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
 |  | 
 |   // Unable to access the host talkgadget. Don't allow the connection, but | 
 |   // schedule a reconnect in case this is a transient problem rather than | 
 |   // an outright block. | 
 |   if (!allow) { | 
 |     reconnect_attempts_++; | 
 |     HOST_LOG << "Talkgadget check failed. Scheduling reconnect. Attempt " | 
 |               << reconnect_attempts_; | 
 |     ScheduleTryReconnect(); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) { | 
 |     HOST_LOG << "Attempting to connect signaling."; | 
 |     oauth_token_getter_->CallWithToken(base::Bind( | 
 |         &SignalingConnector::OnAccessToken, weak_factory_.GetWeakPtr())); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace remoting |