Index: jingle/notifier/communicator/login.cc |
diff --git a/jingle/notifier/communicator/login.cc b/jingle/notifier/communicator/login.cc |
index 7cf4952e092ddef459993932ec0872be6297172b..7d3c4c89819db96fb92331172f587b99e04e7213 100644 |
--- a/jingle/notifier/communicator/login.cc |
+++ b/jingle/notifier/communicator/login.cc |
@@ -7,8 +7,8 @@ |
#include "jingle/notifier/communicator/login.h" |
#include "base/logging.h" |
+#include "base/rand_util.h" |
#include "base/time.h" |
-#include "jingle/notifier/communicator/auto_reconnect.h" |
#include "jingle/notifier/communicator/connection_options.h" |
#include "jingle/notifier/communicator/login_settings.h" |
#include "jingle/notifier/communicator/product_info.h" |
@@ -31,9 +31,6 @@ namespace notifier { |
// Redirect valid for 5 minutes. |
static const int kRedirectTimeoutMinutes = 5; |
-// Disconnect if network stays down for more than 10 seconds. |
-static const int kDisconnectionDelaySecs = 10; |
- |
Login::Login(talk_base::TaskParent* parent, |
bool use_chrome_async_socket, |
const buzz::XmppClientSettings& user_settings, |
@@ -43,8 +40,7 @@ Login::Login(talk_base::TaskParent* parent, |
ServerInformation* server_list, |
int server_count, |
talk_base::FirewallManager* firewall, |
- bool proxy_only, |
- bool previous_login_successful) |
+ bool proxy_only) |
: parent_(parent), |
use_chrome_async_socket_(use_chrome_async_socket), |
login_settings_(new LoginSettings(user_settings, |
@@ -55,38 +51,15 @@ Login::Login(talk_base::TaskParent* parent, |
server_count, |
firewall, |
proxy_only)), |
+ state_(STATE_DISCONNECTED), |
single_attempt_(NULL), |
- successful_connection_(previous_login_successful), |
- state_(STATE_OPENING), |
- redirect_port_(0), |
- unexpected_disconnect_occurred_(false), |
- google_host_(user_settings.host()), |
- google_user_(user_settings.user()) { |
- // Hook up all the signals and observers. |
+ redirect_port_(0) { |
net::NetworkChangeNotifier::AddObserver(this); |
- auto_reconnect_.SignalStartConnection.connect(this, |
- &Login::StartConnection); |
- auto_reconnect_.SignalTimerStartStop.connect( |
- this, |
- &Login::OnAutoReconnectTimerChange); |
- SignalClientStateChange.connect(&auto_reconnect_, |
- &AutoReconnect::OnClientStateChange); |
- SignalIdleChange.connect(&auto_reconnect_, |
- &AutoReconnect::set_idle); |
- SignalPowerSuspended.connect(&auto_reconnect_, |
- &AutoReconnect::OnPowerSuspend); |
- |
- // Then check the initial state of the connection. |
- CheckConnection(); |
+ ResetReconnectState(); |
} |
-// Defined so that the destructors are executed here (and the corresponding |
-// classes don't need to be included in the header file). |
Login::~Login() { |
- if (single_attempt_) { |
- single_attempt_->Abort(); |
- single_attempt_ = NULL; |
- } |
+ Disconnect(); |
net::NetworkChangeNotifier::RemoveObserver(this); |
} |
@@ -103,126 +76,68 @@ void Login::StartConnection() { |
login_settings_->clear_server_override(); |
} |
- if (single_attempt_) { |
- single_attempt_->Abort(); |
- single_attempt_ = NULL; |
- } |
+ Disconnect(); |
+ |
+ LOG(INFO) << "Starting connection..."; |
+ |
single_attempt_ = new SingleLoginAttempt(parent_, |
login_settings_.get(), |
use_chrome_async_socket_, |
- successful_connection_); |
+ true); |
// Do the signaling hook-ups. |
- single_attempt_->SignalLoginFailure.connect(this, &Login::OnLoginFailure); |
- single_attempt_->SignalRedirect.connect(this, &Login::OnRedirect); |
- single_attempt_->SignalClientStateChange.connect( |
- this, |
- &Login::OnClientStateChange); |
single_attempt_->SignalUnexpectedDisconnect.connect( |
this, |
- &Login::OnUnexpectedDisconnect); |
+ &Login::TryReconnect); |
+ single_attempt_->SignalNeedAutoReconnect.connect( |
+ this, |
+ &Login::TryReconnect); |
+ single_attempt_->SignalLoginFailure.connect(this, &Login::OnLoginFailure); |
single_attempt_->SignalLogoff.connect( |
this, |
- &Login::OnLogoff); |
- single_attempt_->SignalNeedAutoReconnect.connect( |
+ &Login::Disconnect); |
+ single_attempt_->SignalRedirect.connect(this, &Login::OnRedirect); |
+ single_attempt_->SignalClientStateChange.connect( |
this, |
- &Login::DoAutoReconnect); |
+ &Login::OnClientStateChange); |
SignalLogInput.repeat(single_attempt_->SignalLogInput); |
SignalLogOutput.repeat(single_attempt_->SignalLogOutput); |
single_attempt_->Start(); |
} |
-const std::string& Login::google_host() const { |
- return google_host_; |
-} |
- |
-const std::string& Login::google_user() const { |
- return google_user_; |
-} |
- |
-const talk_base::ProxyInfo& Login::proxy() const { |
- return proxy_info_; |
-} |
- |
void Login::OnLoginFailure(const LoginFailure& failure) { |
- auto_reconnect_.StopReconnectTimer(); |
- HandleClientStateChange(STATE_CLOSED); |
SignalLoginFailure(failure); |
+ TryReconnect(); |
} |
-void Login::OnLogoff() { |
- HandleClientStateChange(STATE_CLOSED); |
-} |
- |
-void Login::OnClientStateChange(buzz::XmppEngine::State state) { |
- LoginConnectionState new_state = STATE_CLOSED; |
- |
- switch (state) { |
- case buzz::XmppEngine::STATE_NONE: |
- case buzz::XmppEngine::STATE_CLOSED: |
- // Ignore the closed state (because we may be trying the next dns entry). |
- // |
- // But we go to this state for other signals when there is no retry |
- // happening. |
- new_state = state_; |
- break; |
- |
- case buzz::XmppEngine::STATE_START: |
- case buzz::XmppEngine::STATE_OPENING: |
- new_state = STATE_OPENING; |
- break; |
+void Login::OnRedirect(const std::string& redirect_server, int redirect_port) { |
+ DCHECK_NE(redirect_port_, 0); |
- case buzz::XmppEngine::STATE_OPEN: |
- new_state = STATE_OPENED; |
- break; |
+ redirect_time_ = base::Time::Now(); |
+ redirect_server_ = redirect_server; |
+ redirect_port_ = redirect_port; |
- default: |
- DCHECK(false); |
- break; |
- } |
- HandleClientStateChange(new_state); |
+ // Drop the current connection, and start the login process again. |
+ StartConnection(); |
} |
-void Login::HandleClientStateChange(LoginConnectionState new_state) { |
- // Do we need to transition between the retrying and closed states? |
- if (auto_reconnect_.is_retrying()) { |
- if (new_state == STATE_CLOSED) { |
- new_state = STATE_RETRYING; |
- } |
- } else { |
- if (new_state == STATE_RETRYING) { |
- new_state = STATE_CLOSED; |
- } |
+void Login::OnClientStateChange(buzz::XmppEngine::State state) { |
+ // We only care about when we're connected. |
+ if (state == buzz::XmppEngine::STATE_OPEN) { |
+ ResetReconnectState(); |
+ ChangeState(STATE_CONNECTED); |
} |
+} |
- if (new_state != state_) { |
+void Login::ChangeState(LoginConnectionState new_state) { |
+ if (state_ != new_state) { |
state_ = new_state; |
- reset_unexpected_timer_.Stop(); |
- |
- if (state_ == STATE_OPENED) { |
- successful_connection_ = true; |
- |
- google_host_ = single_attempt_->xmpp_client()->jid().domain(); |
- google_user_ = single_attempt_->xmpp_client()->jid().node(); |
- proxy_info_ = single_attempt_->proxy(); |
- |
- reset_unexpected_timer_.Start( |
- base::TimeDelta::FromSeconds(kResetReconnectInfoDelaySec), |
- this, &Login::ResetUnexpectedDisconnect); |
- } |
+ LOG(INFO) << "Signalling new state " << state_; |
SignalClientStateChange(state_); |
} |
} |
-void Login::OnAutoReconnectTimerChange() { |
- if (!single_attempt_ || !single_attempt_->xmpp_client()) { |
- HandleClientStateChange(STATE_CLOSED); |
- return; |
- } |
- OnClientStateChange(single_attempt_->xmpp_client()->GetState()); |
-} |
- |
buzz::XmppClient* Login::xmpp_client() { |
if (!single_attempt_) { |
return NULL; |
@@ -230,122 +145,48 @@ buzz::XmppClient* Login::xmpp_client() { |
return single_attempt_->xmpp_client(); |
} |
-void Login::UseNextConnection() { |
- if (!single_attempt_) { |
- // Just in case, there is an obscure case that causes this to get called |
- // when there is no single_attempt_. |
- return; |
- } |
- single_attempt_->UseNextConnection(); |
-} |
- |
-void Login::UseCurrentConnection() { |
- if (!single_attempt_) { |
- // Just in case, there is an obscure case that causes this to get called |
- // when there is no single_attempt_. |
- return; |
- } |
- single_attempt_->UseCurrentConnection(); |
-} |
- |
-void Login::OnRedirect(const std::string& redirect_server, int redirect_port) { |
- DCHECK_NE(redirect_port_, 0); |
- |
- redirect_time_ = base::Time::Now(); |
- redirect_server_ = redirect_server; |
- redirect_port_ = redirect_port; |
- |
- // Drop the current connection, and start the login process again. |
- StartConnection(); |
-} |
- |
-void Login::OnUnexpectedDisconnect() { |
- reset_unexpected_timer_.Stop(); |
- |
- // Start the login process again. |
- if (unexpected_disconnect_occurred_) { |
- // If we already have received an unexpected disconnect recently, then our |
- // account may have be jailed due to abuse, so we shouldn't make the |
- // situation worse by trying really hard to reconnect. Instead, we'll do |
- // the autoreconnect route, which has exponential back-off. |
- DoAutoReconnect(); |
- return; |
- } |
- StartConnection(); |
- unexpected_disconnect_occurred_ = true; |
-} |
- |
-void Login::ResetUnexpectedDisconnect() { |
- unexpected_disconnect_occurred_ = false; |
+void Login::OnIPAddressChanged() { |
+ LOG(INFO) << "Detected IP address change"; |
+ // Reconnect in 1 to 9 seconds (vary the time a little to try to |
+ // avoid spikey behavior on network hiccups). |
+ reconnect_interval_ = base::TimeDelta::FromSeconds(base::RandInt(1, 9)); |
+ TryReconnect(); |
} |
-void Login::DoAutoReconnect() { |
- bool allow_auto_reconnect = |
- login_settings_->connection_options().auto_reconnect(); |
- // Start the reconnect time before aborting the connection to ensure that |
- // AutoReconnect::is_retrying() is true, so that the Login doesn't |
- // transition to the CLOSED state (which would cause the reconnection timer |
- // to reset and not double). |
- if (allow_auto_reconnect) { |
- auto_reconnect_.StartReconnectTimer(); |
- } |
- |
+void Login::Disconnect() { |
if (single_attempt_) { |
+ LOG(INFO) << "Disconnecting"; |
single_attempt_->Abort(); |
single_attempt_ = NULL; |
} |
- |
- if (!allow_auto_reconnect) { |
- HandleClientStateChange(STATE_CLOSED); |
- return; |
- } |
+ ChangeState(STATE_DISCONNECTED); |
} |
-void Login::OnIPAddressChanged() { |
- LOG(INFO) << "IP address change detected"; |
- CheckConnection(); |
+void Login::ResetReconnectState() { |
+ reconnect_interval_ = |
+ base::TimeDelta::FromSeconds(base::RandInt(5, 25)); |
+ reconnect_timer_.Stop(); |
} |
-void Login::CheckConnection() { |
- // We don't check the connection if we're using ChromeAsyncSocket, |
- // as this code requires a libjingle thread to be running. This |
- // code will go away in a future cleanup CL, anyway. |
- if (!use_chrome_async_socket_) { |
- LOG(INFO) << "Checking connection"; |
- talk_base::PhysicalSocketServer physical; |
- scoped_ptr<talk_base::Socket> socket(physical.CreateSocket(SOCK_STREAM)); |
- bool alive = |
- !socket->Connect(talk_base::SocketAddress("talk.google.com", 5222)); |
- LOG(INFO) << "Network is " << (alive ? "alive" : "not alive"); |
- if (alive) { |
- // Our connection is up. If we have a disconnect timer going, |
- // stop it so we don't disconnect. |
- disconnect_timer_.Stop(); |
- } else { |
- // Our network connection is down. Start the disconnect timer if |
- // it's not already going. Don't disconnect immediately to avoid |
- // constant connection/disconnection due to flaky network |
- // interfaces. |
- if (!disconnect_timer_.IsRunning()) { |
- disconnect_timer_.Start( |
- base::TimeDelta::FromSeconds(kDisconnectionDelaySecs), |
- this, &Login::OnDisconnectTimeout); |
- } |
- } |
- auto_reconnect_.NetworkStateChanged(alive); |
- } |
+void Login::TryReconnect() { |
+ DCHECK_GT(reconnect_interval_.InSeconds(), 0); |
+ Disconnect(); |
+ reconnect_timer_.Stop(); |
+ LOG(INFO) << "Reconnecting in " |
+ << reconnect_interval_.InSeconds() << " seconds"; |
+ reconnect_timer_.Start( |
+ reconnect_interval_, this, &Login::DoReconnect); |
} |
-void Login::OnDisconnectTimeout() { |
- if (state_ != STATE_OPENED) { |
- return; |
+void Login::DoReconnect() { |
+ // Double reconnect time up to 30 minutes. |
+ const base::TimeDelta kMaxReconnectInterval = |
+ base::TimeDelta::FromMinutes(30); |
+ reconnect_interval_ *= 2; |
+ if (reconnect_interval_ > kMaxReconnectInterval) { |
+ reconnect_interval_ = kMaxReconnectInterval; |
} |
- |
- if (single_attempt_) { |
- single_attempt_->Abort(); |
- single_attempt_ = NULL; |
- } |
- |
+ LOG(INFO) << "Reconnecting..."; |
StartConnection(); |
} |