Home | History | Annotate | Download | only in host
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/signaling_connector.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/strings/string_util.h"
     10 #include "google_apis/google_api_keys.h"
     11 #include "net/url_request/url_fetcher.h"
     12 #include "net/url_request/url_request_context_getter.h"
     13 #include "remoting/base/logging.h"
     14 #include "remoting/host/dns_blackhole_checker.h"
     15 
     16 namespace remoting {
     17 
     18 namespace {
     19 
     20 // The delay between reconnect attempts will increase exponentially up
     21 // to the maximum specified here.
     22 const int kMaxReconnectDelaySeconds = 10 * 60;
     23 
     24 }  // namespace
     25 
     26 SignalingConnector::SignalingConnector(
     27     XmppSignalStrategy* signal_strategy,
     28     scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker,
     29     const base::Closure& auth_failed_callback)
     30     : signal_strategy_(signal_strategy),
     31       auth_failed_callback_(auth_failed_callback),
     32       dns_blackhole_checker_(dns_blackhole_checker.Pass()),
     33       reconnect_attempts_(0) {
     34   DCHECK(!auth_failed_callback_.is_null());
     35   DCHECK(dns_blackhole_checker_.get());
     36   net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
     37   net::NetworkChangeNotifier::AddIPAddressObserver(this);
     38   signal_strategy_->AddListener(this);
     39   ScheduleTryReconnect();
     40 }
     41 
     42 SignalingConnector::~SignalingConnector() {
     43   signal_strategy_->RemoveListener(this);
     44   net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
     45   net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
     46 }
     47 
     48 void SignalingConnector::EnableOAuth(OAuthTokenGetter* oauth_token_getter) {
     49   oauth_token_getter_ = oauth_token_getter;
     50 }
     51 
     52 void SignalingConnector::OnSignalStrategyStateChange(
     53     SignalStrategy::State state) {
     54   DCHECK(CalledOnValidThread());
     55 
     56   if (state == SignalStrategy::CONNECTED) {
     57     HOST_LOG << "Signaling connected.";
     58     reconnect_attempts_ = 0;
     59   } else if (state == SignalStrategy::DISCONNECTED) {
     60     HOST_LOG << "Signaling disconnected.";
     61     reconnect_attempts_++;
     62 
     63     // If authentication failed then we have an invalid OAuth token,
     64     // inform the upper layer about it.
     65     if (signal_strategy_->GetError() == SignalStrategy::AUTHENTICATION_FAILED) {
     66       auth_failed_callback_.Run();
     67     } else {
     68       ScheduleTryReconnect();
     69     }
     70   }
     71 }
     72 
     73 bool SignalingConnector::OnSignalStrategyIncomingStanza(
     74     const buzz::XmlElement* stanza) {
     75   return false;
     76 }
     77 
     78 void SignalingConnector::OnConnectionTypeChanged(
     79     net::NetworkChangeNotifier::ConnectionType type) {
     80   DCHECK(CalledOnValidThread());
     81   if (type != net::NetworkChangeNotifier::CONNECTION_NONE &&
     82       signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
     83     HOST_LOG << "Network state changed to online.";
     84     ResetAndTryReconnect();
     85   }
     86 }
     87 
     88 void SignalingConnector::OnIPAddressChanged() {
     89   DCHECK(CalledOnValidThread());
     90   if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
     91     HOST_LOG << "IP address has changed.";
     92     ResetAndTryReconnect();
     93   }
     94 }
     95 
     96 void SignalingConnector::OnAccessToken(OAuthTokenGetter::Status status,
     97                                        const std::string& user_email,
     98                                        const std::string& access_token) {
     99   DCHECK(CalledOnValidThread());
    100 
    101   if (status == OAuthTokenGetter::AUTH_ERROR) {
    102     auth_failed_callback_.Run();
    103     return;
    104   } else if (status == OAuthTokenGetter::NETWORK_ERROR) {
    105     OnNetworkError();
    106     return;
    107   }
    108 
    109   DCHECK_EQ(status, OAuthTokenGetter::SUCCESS);
    110   HOST_LOG << "Received user info.";
    111 
    112   signal_strategy_->SetAuthInfo(user_email, access_token, "oauth2");
    113 
    114   // Now that we've refreshed the token and verified that it's for the correct
    115   // user account, try to connect using the new token.
    116   DCHECK_EQ(signal_strategy_->GetState(), SignalStrategy::DISCONNECTED);
    117   signal_strategy_->Connect();
    118 }
    119 
    120 void SignalingConnector::OnNetworkError() {
    121   DCHECK(CalledOnValidThread());
    122   reconnect_attempts_++;
    123   ScheduleTryReconnect();
    124 }
    125 
    126 void SignalingConnector::ScheduleTryReconnect() {
    127   DCHECK(CalledOnValidThread());
    128   if (timer_.IsRunning() || net::NetworkChangeNotifier::IsOffline())
    129     return;
    130   int delay_s = std::min(1 << reconnect_attempts_,
    131                          kMaxReconnectDelaySeconds);
    132   timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay_s),
    133                this, &SignalingConnector::TryReconnect);
    134 }
    135 
    136 void SignalingConnector::ResetAndTryReconnect() {
    137   DCHECK(CalledOnValidThread());
    138   signal_strategy_->Disconnect();
    139   reconnect_attempts_ = 0;
    140   timer_.Stop();
    141   ScheduleTryReconnect();
    142 }
    143 
    144 void SignalingConnector::TryReconnect() {
    145   DCHECK(CalledOnValidThread());
    146   DCHECK(dns_blackhole_checker_.get());
    147 
    148   // This will check if this machine is allowed to access the chromoting
    149   // host talkgadget.
    150   dns_blackhole_checker_->CheckForDnsBlackhole(
    151       base::Bind(&SignalingConnector::OnDnsBlackholeCheckerDone,
    152                  base::Unretained(this)));
    153 }
    154 
    155 void SignalingConnector::OnDnsBlackholeCheckerDone(bool allow) {
    156   DCHECK(CalledOnValidThread());
    157 
    158   // Unable to access the host talkgadget. Don't allow the connection, but
    159   // schedule a reconnect in case this is a transient problem rather than
    160   // an outright block.
    161   if (!allow) {
    162     reconnect_attempts_++;
    163     HOST_LOG << "Talkgadget check failed. Scheduling reconnect. Attempt "
    164               << reconnect_attempts_;
    165     ScheduleTryReconnect();
    166     return;
    167   }
    168 
    169   if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
    170     HOST_LOG << "Attempting to connect signaling.";
    171     oauth_token_getter_->CallWithToken(
    172         base::Bind(&SignalingConnector::OnAccessToken, AsWeakPtr()));
    173   }
    174 }
    175 
    176 }  // namespace remoting
    177