blob: ffe1cee3db8bae73a26218f796f3a990992329b1 [file] [log] [blame]
// 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 {
constexpr base::TimeDelta kBackoffResetDelay = base::TimeDelta::FromSeconds(30);
// 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();
backoff_reset_timer_.Start(FROM_HERE, kBackoffResetDelay, this,
&SignalingConnector::ResetBackoff);
} else if (state == SignalStrategy::DISCONNECTED) {
HOST_LOG << "Signaling disconnected. error="
<< SignalStrategyErrorToString(signal_strategy_->GetError());
backoff_reset_timer_.Stop();
reconnect_attempts_++;
if (signal_strategy_->GetError() == SignalStrategy::AUTHENTICATION_FAILED)
oauth_token_getter_->InvalidateCache();
ScheduleTryReconnect();
}
}
bool SignalingConnector::OnSignalStrategyIncomingStanza(
const jingle_xmpp::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();
ResetBackoff();
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::BindOnce(
&SignalingConnector::OnAccessToken, weak_factory_.GetWeakPtr()));
}
}
void SignalingConnector::ResetBackoff() {
backoff_reset_timer_.Stop();
reconnect_attempts_ = 0;
}
} // namespace remoting