OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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 <string> |
| 6 |
| 7 #include "chrome/browser/sync/notifier/communicator/login.h" |
| 8 |
| 9 #include "chrome/browser/sync/notifier/base/network_status_detector_task.h" |
| 10 #include "chrome/browser/sync/notifier/base/time.h" |
| 11 #include "chrome/browser/sync/notifier/base/timer.h" |
| 12 #include "chrome/browser/sync/notifier/communicator/auto_reconnect.h" |
| 13 #include "chrome/browser/sync/notifier/communicator/connection_options.h" |
| 14 #include "chrome/browser/sync/notifier/communicator/login_settings.h" |
| 15 #include "chrome/browser/sync/notifier/communicator/product_info.h" |
| 16 #include "chrome/browser/sync/notifier/communicator/single_login_attempt.h" |
| 17 #include "talk/base/common.h" |
| 18 #include "talk/base/firewallsocketserver.h" |
| 19 #include "talk/base/logging.h" |
| 20 #include "talk/base/taskrunner.h" |
| 21 #include "talk/xmllite/xmlelement.h" |
| 22 #include "talk/xmpp/asyncsocket.h" |
| 23 #include "talk/xmpp/prexmppauth.h" |
| 24 #include "talk/xmpp/xmppclient.h" |
| 25 #include "talk/xmpp/xmppclientsettings.h" |
| 26 #include "talk/xmpp/xmppengine.h" |
| 27 |
| 28 namespace notifier { |
| 29 |
| 30 // redirect valid for 5 minutes |
| 31 static const time64 kRedirectTimeoutNs = 5 * kMinsTo100ns; |
| 32 |
| 33 // Disconnect if network stays down for more than 10 seconds. |
| 34 static const int kDisconnectionDelaySecs = 10; |
| 35 |
| 36 Login::Login(talk_base::Task* parent, |
| 37 const buzz::XmppClientSettings& user_settings, |
| 38 const ConnectionOptions& options, |
| 39 std::string lang, |
| 40 ServerInformation* server_list, |
| 41 int server_count, |
| 42 NetworkStatusDetectorTask* network_status, |
| 43 talk_base::FirewallManager* firewall, |
| 44 bool no_gaia_auth, |
| 45 bool proxy_only, |
| 46 bool previous_login_successful) |
| 47 : login_settings_(new LoginSettings(user_settings, |
| 48 options, |
| 49 lang, |
| 50 server_list, |
| 51 server_count, |
| 52 firewall, |
| 53 no_gaia_auth, |
| 54 proxy_only)), |
| 55 single_attempt_(NULL), |
| 56 successful_connection_(previous_login_successful), |
| 57 parent_(parent), |
| 58 state_(STATE_OPENING), |
| 59 redirect_time_ns_(0), |
| 60 redirect_port_(0), |
| 61 unexpected_disconnect_occurred_(false), |
| 62 reset_unexpected_timer_(NULL), |
| 63 google_host_(user_settings.host()), |
| 64 google_user_(user_settings.user()), |
| 65 disconnect_timer_(NULL) { |
| 66 if (!network_status) { |
| 67 network_status = NetworkStatusDetectorTask::Create(parent_); |
| 68 if (network_status) { |
| 69 // On linux we don't have an implementation of NetworkStatusDetectorTask. |
| 70 network_status->Start(); |
| 71 } |
| 72 } |
| 73 network_status->SignalNetworkStateDetected.connect( |
| 74 this, &Login::OnNetworkStateDetected); |
| 75 auto_reconnect_.reset(new AutoReconnect(parent_, network_status)); |
| 76 auto_reconnect_->SignalStartConnection.connect(this, |
| 77 &Login::StartConnection); |
| 78 auto_reconnect_->SignalTimerStartStop.connect( |
| 79 this, |
| 80 &Login::OnAutoReconnectTimerChange); |
| 81 SignalClientStateChange.connect(auto_reconnect_.get(), |
| 82 &AutoReconnect::OnClientStateChange); |
| 83 SignalIdleChange.connect(auto_reconnect_.get(), |
| 84 &AutoReconnect::set_idle); |
| 85 SignalPowerSuspended.connect(auto_reconnect_.get(), |
| 86 &AutoReconnect::OnPowerSuspend); |
| 87 } |
| 88 |
| 89 // defined so that the destructors are executed here (and |
| 90 // the corresponding classes don't need to be included in |
| 91 // the header file) |
| 92 Login::~Login() { |
| 93 if (single_attempt_) { |
| 94 single_attempt_->Abort(); |
| 95 single_attempt_ = NULL; |
| 96 } |
| 97 } |
| 98 |
| 99 void Login::StartConnection() { |
| 100 // If there is a server redirect, use it. |
| 101 if (GetCurrent100NSTime() < redirect_time_ns_ + kRedirectTimeoutNs) { |
| 102 // Override server/port with redirect values |
| 103 talk_base::SocketAddress server_override; |
| 104 server_override.SetIP(redirect_server_, false); |
| 105 ASSERT(redirect_port_ != 0); |
| 106 server_override.SetPort(redirect_port_); |
| 107 login_settings_->set_server_override(server_override); |
| 108 } else { |
| 109 login_settings_->clear_server_override(); |
| 110 } |
| 111 |
| 112 if (single_attempt_) { |
| 113 single_attempt_->Abort(); |
| 114 single_attempt_ = NULL; |
| 115 } |
| 116 single_attempt_ = new SingleLoginAttempt(parent_, |
| 117 login_settings_.get(), |
| 118 successful_connection_); |
| 119 |
| 120 // Do the signaling hook-ups. |
| 121 single_attempt_->SignalLoginFailure.connect(this, &Login::OnLoginFailure); |
| 122 single_attempt_->SignalRedirect.connect(this, &Login::OnRedirect); |
| 123 single_attempt_->SignalClientStateChange.connect( |
| 124 this, |
| 125 &Login::OnClientStateChange) ; |
| 126 single_attempt_->SignalUnexpectedDisconnect.connect( |
| 127 this, |
| 128 &Login::OnUnexpectedDisconnect); |
| 129 single_attempt_->SignalLogoff.connect( |
| 130 this, |
| 131 &Login::OnLogoff); |
| 132 single_attempt_->SignalNeedAutoReconnect.connect( |
| 133 this, |
| 134 &Login::DoAutoReconnect); |
| 135 SignalLogInput.repeat(single_attempt_->SignalLogInput); |
| 136 SignalLogOutput.repeat(single_attempt_->SignalLogOutput); |
| 137 |
| 138 single_attempt_->Start(); |
| 139 } |
| 140 |
| 141 const std::string& Login::google_host() const { |
| 142 return google_host_; |
| 143 } |
| 144 |
| 145 const std::string& Login::google_user() const { |
| 146 return google_user_; |
| 147 } |
| 148 |
| 149 const talk_base::ProxyInfo& Login::proxy() const { |
| 150 return proxy_info_; |
| 151 } |
| 152 |
| 153 void Login::OnLoginFailure(const LoginFailure& failure) { |
| 154 auto_reconnect_->StopReconnectTimer(); |
| 155 HandleClientStateChange(STATE_CLOSED); |
| 156 SignalLoginFailure(failure); |
| 157 } |
| 158 |
| 159 void Login::OnLogoff() { |
| 160 HandleClientStateChange(STATE_CLOSED); |
| 161 } |
| 162 |
| 163 void Login::OnClientStateChange(buzz::XmppEngine::State state) { |
| 164 ConnectionState new_state = STATE_CLOSED; |
| 165 |
| 166 switch (state) { |
| 167 case buzz::XmppEngine::STATE_NONE: |
| 168 case buzz::XmppEngine::STATE_CLOSED: |
| 169 // Ignore the closed state (because |
| 170 // we may be trying the next dns entry). |
| 171 // |
| 172 // But we go to this state for other |
| 173 // signals when there is no retry happening. |
| 174 new_state = state_; |
| 175 break; |
| 176 |
| 177 case buzz::XmppEngine::STATE_START: |
| 178 case buzz::XmppEngine::STATE_OPENING: |
| 179 new_state = STATE_OPENING; |
| 180 break; |
| 181 |
| 182 case buzz::XmppEngine::STATE_OPEN: |
| 183 new_state = STATE_OPENED; |
| 184 break; |
| 185 |
| 186 default: |
| 187 ASSERT(false); |
| 188 break; |
| 189 } |
| 190 HandleClientStateChange(new_state); |
| 191 } |
| 192 |
| 193 void Login::HandleClientStateChange(ConnectionState new_state) { |
| 194 // Do we need to transition between the retrying and closed states? |
| 195 if (auto_reconnect_->is_retrying()) { |
| 196 if (new_state == STATE_CLOSED) { |
| 197 new_state = STATE_RETRYING; |
| 198 } |
| 199 } else { |
| 200 if (new_state == STATE_RETRYING) { |
| 201 new_state = STATE_CLOSED; |
| 202 } |
| 203 } |
| 204 |
| 205 if (new_state != state_) { |
| 206 state_ = new_state; |
| 207 if (reset_unexpected_timer_) { |
| 208 reset_unexpected_timer_->Abort(); |
| 209 reset_unexpected_timer_ = NULL; |
| 210 } |
| 211 |
| 212 if (state_ == STATE_OPENED) { |
| 213 successful_connection_ = true; |
| 214 |
| 215 google_host_ = single_attempt_->xmpp_client()->jid().domain(); |
| 216 google_user_ = single_attempt_->xmpp_client()->jid().node(); |
| 217 proxy_info_ = single_attempt_->proxy(); |
| 218 |
| 219 reset_unexpected_timer_ = new Timer(parent_, |
| 220 kResetReconnectInfoDelaySec, |
| 221 false); // repeat |
| 222 reset_unexpected_timer_->SignalTimeout.connect( |
| 223 this, |
| 224 &Login::ResetUnexpectedDisconnect); |
| 225 } |
| 226 SignalClientStateChange(state_); |
| 227 } |
| 228 } |
| 229 |
| 230 void Login::OnAutoReconnectTimerChange() { |
| 231 if (!single_attempt_ || !single_attempt_->xmpp_client()) { |
| 232 HandleClientStateChange(STATE_CLOSED); |
| 233 return; |
| 234 } |
| 235 OnClientStateChange(single_attempt_->xmpp_client()->GetState()); |
| 236 } |
| 237 |
| 238 buzz::XmppClient* Login::xmpp_client() { |
| 239 if (!single_attempt_) { |
| 240 return NULL; |
| 241 } |
| 242 return single_attempt_->xmpp_client(); |
| 243 } |
| 244 |
| 245 int Login::seconds_until_reconnect() const { |
| 246 return auto_reconnect_->seconds_until(); |
| 247 } |
| 248 |
| 249 void Login::UseNextConnection() { |
| 250 if (!single_attempt_) { |
| 251 // Just in case, there is an obscure case that causes |
| 252 // this to get called when there is no single_attempt_. |
| 253 return; |
| 254 } |
| 255 single_attempt_->UseNextConnection(); |
| 256 } |
| 257 |
| 258 void Login::UseCurrentConnection() { |
| 259 if (!single_attempt_) { |
| 260 // Just in case, there is an obscure case that causes |
| 261 // this to get called when there is no single_attempt_. |
| 262 return; |
| 263 } |
| 264 single_attempt_->UseCurrentConnection(); |
| 265 } |
| 266 |
| 267 void Login::OnRedirect(const std::string& redirect_server, int redirect_port) { |
| 268 ASSERT(redirect_port_ != 0); |
| 269 |
| 270 redirect_time_ns_ = GetCurrent100NSTime(); |
| 271 redirect_server_ = redirect_server; |
| 272 redirect_port_ = redirect_port; |
| 273 |
| 274 // Drop the current connection, and start the login process again |
| 275 StartConnection(); |
| 276 } |
| 277 |
| 278 void Login::OnUnexpectedDisconnect() { |
| 279 if (reset_unexpected_timer_) { |
| 280 reset_unexpected_timer_->Abort(); |
| 281 reset_unexpected_timer_ = NULL; |
| 282 } |
| 283 |
| 284 // Start the login process again |
| 285 if (unexpected_disconnect_occurred_) { |
| 286 // If we already have received an unexpected disconnect recently, |
| 287 // then our account may have be jailed due to abuse, so we shouldn't |
| 288 // make the situation worse by trying really hard to reconnect. |
| 289 // Instead, we'll do the autoreconnect route, which has exponential |
| 290 // back-off. |
| 291 DoAutoReconnect(); |
| 292 return; |
| 293 } |
| 294 StartConnection(); |
| 295 unexpected_disconnect_occurred_ = true; |
| 296 } |
| 297 |
| 298 void Login::ResetUnexpectedDisconnect() { |
| 299 reset_unexpected_timer_ = NULL; |
| 300 unexpected_disconnect_occurred_ = false; |
| 301 } |
| 302 |
| 303 void Login::DoAutoReconnect() { |
| 304 bool allow_auto_reconnect = |
| 305 login_settings_->connection_options().auto_reconnect(); |
| 306 // Start the reconnect time before aborting the connection |
| 307 // to ensure that AutoReconnect::is_retrying() is true, so |
| 308 // that the Login doesn't transition to the CLOSED state |
| 309 // (which would cause the reconnection timer to reset |
| 310 // and not double). |
| 311 if (allow_auto_reconnect) { |
| 312 auto_reconnect_->StartReconnectTimer(); |
| 313 } |
| 314 |
| 315 if (single_attempt_) { |
| 316 single_attempt_->Abort(); |
| 317 single_attempt_ = NULL; |
| 318 } |
| 319 |
| 320 if (!allow_auto_reconnect) { |
| 321 HandleClientStateChange(STATE_CLOSED); |
| 322 return; |
| 323 } |
| 324 } |
| 325 |
| 326 void Login::OnNetworkStateDetected(bool was_alive, bool is_alive) { |
| 327 if (was_alive && !is_alive) { |
| 328 // Our network connection just went down. |
| 329 // Setup a timer to disconnect. Don't disconnect immediately to avoid |
| 330 // constant connection/disconnection due to flaky network interfaces. |
| 331 ASSERT(disconnect_timer_ == NULL); |
| 332 disconnect_timer_ = new Timer(parent_, kDisconnectionDelaySecs, false); |
| 333 disconnect_timer_->SignalTimeout.connect(this, |
| 334 &Login::OnDisconnectTimeout); |
| 335 } else if (!was_alive && is_alive) { |
| 336 // Our connection has come back up. If we have a disconnect timer going, |
| 337 // abort it so we don't disconnect. |
| 338 if (disconnect_timer_) { |
| 339 disconnect_timer_->Abort(); |
| 340 // It will free itself. |
| 341 disconnect_timer_ = NULL; |
| 342 } |
| 343 } |
| 344 } |
| 345 |
| 346 void Login::OnDisconnectTimeout() { |
| 347 disconnect_timer_ = NULL; |
| 348 |
| 349 if (state_ != STATE_OPENED) { |
| 350 return; |
| 351 } |
| 352 |
| 353 if (single_attempt_) { |
| 354 single_attempt_->Abort(); |
| 355 single_attempt_ = NULL; |
| 356 } |
| 357 |
| 358 StartConnection(); |
| 359 } |
| 360 |
| 361 } // namespace notifier |
OLD | NEW |