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"
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"
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;
29 SignalingConnector::OAuthCredentials::OAuthCredentials(
30 const std::string
& login_value
,
31 const std::string
& refresh_token_value
,
32 bool is_service_account
)
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();
86 ScheduleTryReconnect();
91 bool SignalingConnector::OnSignalStrategyIncomingStanza(
92 const buzz::XmlElement
* stanza
) {
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
) {
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 "
143 auth_failed_callback_
.Run();
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: "
169 refreshing_oauth_token_
= false;
170 reconnect_attempts_
++;
171 ScheduleTryReconnect();
174 void SignalingConnector::ScheduleTryReconnect() {
175 DCHECK(CalledOnValidThread());
176 if (timer_
.IsRunning() || net::NetworkChangeNotifier::IsOffline())
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;
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
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.
210 reconnect_attempts_
++;
211 HOST_LOG
<< "Talkgadget check failed. Scheduling reconnect. Attempt "
212 << reconnect_attempts_
;
213 ScheduleTryReconnect();
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
) {
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
;
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
,
258 } // namespace remoting