Quota: Return a pair of usage and granted_quota on requesting quota.
[chromium-blink-merge.git] / remoting / host / signaling_connector.cc
blob710c3a22a8e852e6e3ff320977952b6d79dcdf4b
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.
5 #include "remoting/host/signaling_connector.h"
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"
16 namespace remoting {
18 namespace {
20 // The delay between reconnect attempts will increase exponentially up
21 // to the maximum specified here.
22 const int kMaxReconnectDelaySeconds = 10 * 60;
24 // Time when we we try to update OAuth token before its expiration.
25 const int kTokenUpdateTimeBeforeExpirySeconds = 60;
27 } // namespace
29 SignalingConnector::OAuthCredentials::OAuthCredentials(
30 const std::string& login_value,
31 const std::string& refresh_token_value,
32 bool is_service_account)
33 : login(login_value),
34 refresh_token(refresh_token_value),
35 is_service_account(is_service_account) {
38 SignalingConnector::SignalingConnector(
39 XmppSignalStrategy* signal_strategy,
40 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
41 scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker,
42 const base::Closure& auth_failed_callback)
43 : signal_strategy_(signal_strategy),
44 url_request_context_getter_(url_request_context_getter),
45 auth_failed_callback_(auth_failed_callback),
46 dns_blackhole_checker_(dns_blackhole_checker.Pass()),
47 reconnect_attempts_(0),
48 refreshing_oauth_token_(false) {
49 DCHECK(!auth_failed_callback_.is_null());
50 DCHECK(dns_blackhole_checker_.get());
51 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
52 net::NetworkChangeNotifier::AddIPAddressObserver(this);
53 signal_strategy_->AddListener(this);
54 ScheduleTryReconnect();
57 SignalingConnector::~SignalingConnector() {
58 signal_strategy_->RemoveListener(this);
59 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
60 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
63 void SignalingConnector::EnableOAuth(
64 scoped_ptr<OAuthCredentials> oauth_credentials) {
65 oauth_credentials_ = oauth_credentials.Pass();
66 gaia_oauth_client_.reset(
67 new gaia::GaiaOAuthClient(url_request_context_getter_.get()));
70 void SignalingConnector::OnSignalStrategyStateChange(
71 SignalStrategy::State state) {
72 DCHECK(CalledOnValidThread());
74 if (state == SignalStrategy::CONNECTED) {
75 HOST_LOG << "Signaling connected.";
76 reconnect_attempts_ = 0;
77 } else if (state == SignalStrategy::DISCONNECTED) {
78 HOST_LOG << "Signaling disconnected.";
79 reconnect_attempts_++;
81 // If authentication failed then we have an invalid OAuth token,
82 // inform the upper layer about it.
83 if (signal_strategy_->GetError() == SignalStrategy::AUTHENTICATION_FAILED) {
84 auth_failed_callback_.Run();
85 } else {
86 ScheduleTryReconnect();
91 bool SignalingConnector::OnSignalStrategyIncomingStanza(
92 const buzz::XmlElement* stanza) {
93 return false;
96 void SignalingConnector::OnConnectionTypeChanged(
97 net::NetworkChangeNotifier::ConnectionType type) {
98 DCHECK(CalledOnValidThread());
99 if (type != net::NetworkChangeNotifier::CONNECTION_NONE &&
100 signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
101 HOST_LOG << "Network state changed to online.";
102 ResetAndTryReconnect();
106 void SignalingConnector::OnIPAddressChanged() {
107 DCHECK(CalledOnValidThread());
108 if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
109 HOST_LOG << "IP address has changed.";
110 ResetAndTryReconnect();
114 void SignalingConnector::OnGetTokensResponse(const std::string& user_email,
115 const std::string& access_token,
116 int expires_seconds) {
117 NOTREACHED();
120 void SignalingConnector::OnRefreshTokenResponse(
121 const std::string& access_token,
122 int expires_seconds) {
123 DCHECK(CalledOnValidThread());
124 DCHECK(oauth_credentials_.get());
125 HOST_LOG << "Received OAuth token.";
127 oauth_access_token_ = access_token;
128 auth_token_expiry_time_ = base::Time::Now() +
129 base::TimeDelta::FromSeconds(expires_seconds) -
130 base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds);
132 gaia_oauth_client_->GetUserEmail(access_token, 1, this);
135 void SignalingConnector::OnGetUserEmailResponse(const std::string& user_email) {
136 DCHECK(CalledOnValidThread());
137 DCHECK(oauth_credentials_.get());
138 HOST_LOG << "Received user info.";
140 if (user_email != oauth_credentials_->login) {
141 LOG(ERROR) << "OAuth token and email address do not refer to "
142 "the same account.";
143 auth_failed_callback_.Run();
144 return;
147 signal_strategy_->SetAuthInfo(oauth_credentials_->login,
148 oauth_access_token_, "oauth2");
149 refreshing_oauth_token_ = false;
151 // Now that we've refreshed the token and verified that it's for the correct
152 // user account, try to connect using the new token.
153 DCHECK_EQ(signal_strategy_->GetState(), SignalStrategy::DISCONNECTED);
154 signal_strategy_->Connect();
157 void SignalingConnector::OnOAuthError() {
158 DCHECK(CalledOnValidThread());
159 LOG(ERROR) << "OAuth: invalid credentials.";
160 refreshing_oauth_token_ = false;
161 reconnect_attempts_++;
162 auth_failed_callback_.Run();
165 void SignalingConnector::OnNetworkError(int response_code) {
166 DCHECK(CalledOnValidThread());
167 LOG(ERROR) << "Network error when trying to update OAuth token: "
168 << response_code;
169 refreshing_oauth_token_ = false;
170 reconnect_attempts_++;
171 ScheduleTryReconnect();
174 void SignalingConnector::ScheduleTryReconnect() {
175 DCHECK(CalledOnValidThread());
176 if (timer_.IsRunning() || net::NetworkChangeNotifier::IsOffline())
177 return;
178 int delay_s = std::min(1 << reconnect_attempts_,
179 kMaxReconnectDelaySeconds);
180 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay_s),
181 this, &SignalingConnector::TryReconnect);
184 void SignalingConnector::ResetAndTryReconnect() {
185 DCHECK(CalledOnValidThread());
186 signal_strategy_->Disconnect();
187 reconnect_attempts_ = 0;
188 timer_.Stop();
189 ScheduleTryReconnect();
192 void SignalingConnector::TryReconnect() {
193 DCHECK(CalledOnValidThread());
194 DCHECK(dns_blackhole_checker_.get());
196 // This will check if this machine is allowed to access the chromoting
197 // host talkgadget.
198 dns_blackhole_checker_->CheckForDnsBlackhole(
199 base::Bind(&SignalingConnector::OnDnsBlackholeCheckerDone,
200 base::Unretained(this)));
203 void SignalingConnector::OnDnsBlackholeCheckerDone(bool allow) {
204 DCHECK(CalledOnValidThread());
206 // Unable to access the host talkgadget. Don't allow the connection, but
207 // schedule a reconnect in case this is a transient problem rather than
208 // an outright block.
209 if (!allow) {
210 reconnect_attempts_++;
211 HOST_LOG << "Talkgadget check failed. Scheduling reconnect. Attempt "
212 << reconnect_attempts_;
213 ScheduleTryReconnect();
214 return;
217 if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
218 bool need_new_auth_token = oauth_credentials_.get() &&
219 (auth_token_expiry_time_.is_null() ||
220 base::Time::Now() >= auth_token_expiry_time_);
221 if (need_new_auth_token) {
222 RefreshOAuthToken();
223 } else {
224 HOST_LOG << "Attempting to connect signaling.";
225 signal_strategy_->Connect();
230 void SignalingConnector::RefreshOAuthToken() {
231 DCHECK(CalledOnValidThread());
232 HOST_LOG << "Refreshing OAuth token.";
233 DCHECK(!refreshing_oauth_token_);
235 // Service accounts use different API keys, as they use the client app flow.
236 google_apis::OAuth2Client oauth2_client;
237 if (oauth_credentials_->is_service_account) {
238 oauth2_client = google_apis::CLIENT_REMOTING_HOST;
239 } else {
240 oauth2_client = google_apis::CLIENT_REMOTING;
243 gaia::OAuthClientInfo client_info = {
244 google_apis::GetOAuth2ClientID(oauth2_client),
245 google_apis::GetOAuth2ClientSecret(oauth2_client),
246 // Redirect URL is only used when getting tokens from auth code. It
247 // is not required when getting access tokens.
251 refreshing_oauth_token_ = true;
252 std::vector<std::string> empty_scope_list; // (Use scope from refresh token.)
253 gaia_oauth_client_->RefreshToken(
254 client_info, oauth_credentials_->refresh_token, empty_scope_list,
255 1, this);
258 } // namespace remoting