| Index: google_apis/gcm/engine/connection_factory_impl.cc
|
| diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
|
| index fcab2c2de251689427705b6a9eaf8374f88e8161..3c2e412ce38922e0e39a9c1ad815f34e70f4969c 100644
|
| --- a/google_apis/gcm/engine/connection_factory_impl.cc
|
| +++ b/google_apis/gcm/engine/connection_factory_impl.cc
|
| @@ -29,6 +29,15 @@ const int kReadTimeoutMs = 30000; // 30 seconds.
|
| // as if it was transient).
|
| const int kConnectionResetWindowSecs = 10; // 10 seconds.
|
|
|
| +// Decides whether the last login was within kConnectionResetWindowSecs of now
|
| +// or not.
|
| +bool ShouldRestorePreviousBackoff(const base::TimeTicks& login_time,
|
| + const base::TimeTicks& now_ticks) {
|
| + return !login_time.is_null() &&
|
| + now_ticks - login_time <=
|
| + base::TimeDelta::FromSeconds(kConnectionResetWindowSecs);
|
| +}
|
| +
|
| } // namespace
|
|
|
| ConnectionFactoryImpl::ConnectionFactoryImpl(
|
| @@ -41,6 +50,7 @@ ConnectionFactoryImpl::ConnectionFactoryImpl(
|
| network_session_(network_session),
|
| net_log_(net_log),
|
| connecting_(false),
|
| + logging_in_(false),
|
| weak_ptr_factory_(this) {
|
| }
|
|
|
| @@ -98,21 +108,52 @@ bool ConnectionFactoryImpl::IsEndpointReachable() const {
|
| !connecting_;
|
| }
|
|
|
| -void ConnectionFactoryImpl::SignalConnectionReset() {
|
| +void ConnectionFactoryImpl::SignalConnectionReset(
|
| + ConnectionResetReason reason) {
|
| + // A failure can trigger multiple resets, so no need to do anything if a
|
| + // connection is already in progress.
|
| if (connecting_)
|
| - return; // Already attempting to reconnect.
|
| + return;
|
| +
|
| + UMA_HISTOGRAM_ENUMERATION("GCM.ConnectionResetReason",
|
| + reason,
|
| + CONNECTION_RESET_COUNT);
|
| + if (!last_login_time_.is_null()) {
|
| + UMA_HISTOGRAM_CUSTOM_TIMES("GCM.ConnectionUpTime",
|
| + NowTicks() - last_login_time_,
|
| + base::TimeDelta::FromSeconds(1),
|
| + base::TimeDelta::FromHours(24),
|
| + 50);
|
| + // |last_login_time_| will be reset below, before attempting the new
|
| + // connection.
|
| + }
|
|
|
| if (connection_handler_)
|
| connection_handler_->Reset();
|
|
|
| - if (!backoff_reset_time_.is_null() &&
|
| - NowTicks() - backoff_reset_time_ <=
|
| - base::TimeDelta::FromSeconds(kConnectionResetWindowSecs)) {
|
| + if (socket_handle_.socket() && socket_handle_.socket()->IsConnected())
|
| + socket_handle_.socket()->Disconnect();
|
| + socket_handle_.Reset();
|
| +
|
| + if (logging_in_) {
|
| + // Failures prior to login completion just reuse the existing backoff entry.
|
| + logging_in_ = false;
|
| + backoff_entry_->InformOfRequest(false);
|
| + } else if (reason == LOGIN_FAILURE ||
|
| + ShouldRestorePreviousBackoff(last_login_time_, NowTicks())) {
|
| + // Failures due to login, or within the reset window of a login, restore
|
| + // the backoff entry that was saved off at login completion time.
|
| backoff_entry_.swap(previous_backoff_);
|
| backoff_entry_->InformOfRequest(false);
|
| + } else {
|
| + // We shouldn't be in backoff in thise case.
|
| + DCHECK(backoff_entry_->CanDiscard());
|
| }
|
| - backoff_reset_time_ = base::TimeTicks();
|
| - previous_backoff_->Reset();
|
| +
|
| + // At this point the last login time has been consumed or deemed irrelevant,
|
| + // reset it.
|
| + last_login_time_ = base::TimeTicks();
|
| +
|
| Connect();
|
| }
|
|
|
| @@ -143,10 +184,8 @@ void ConnectionFactoryImpl::OnIPAddressChanged() {
|
| }
|
|
|
| void ConnectionFactoryImpl::ConnectImpl() {
|
| - DCHECK(!connection_handler_->CanSendMessage());
|
| - if (socket_handle_.socket() && socket_handle_.socket()->IsConnected())
|
| - socket_handle_.socket()->Disconnect();
|
| - socket_handle_.Reset();
|
| + DCHECK(connecting_);
|
| + DCHECK(!socket_handle_.socket());
|
|
|
| // TODO(zea): resolve proxies.
|
| net::ProxyInfo proxy_info;
|
| @@ -190,6 +229,8 @@ base::TimeTicks ConnectionFactoryImpl::NowTicks() {
|
| }
|
|
|
| void ConnectionFactoryImpl::OnConnectDone(int result) {
|
| + UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", (result == net::OK));
|
| +
|
| if (result != net::OK) {
|
| LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result;
|
| backoff_entry_->InformOfRequest(false);
|
| @@ -198,31 +239,29 @@ void ConnectionFactoryImpl::OnConnectDone(int result) {
|
| return;
|
| }
|
|
|
| - DVLOG(1) << "MCS endpoint socket connection success, starting handshake.";
|
| + connecting_ = false;
|
| + logging_in_ = true;
|
| + DVLOG(1) << "MCS endpoint socket connection success, starting login.";
|
| InitHandler();
|
| }
|
|
|
| void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) {
|
| - if (result == net::OK) {
|
| - // Handshake succeeded, reset the backoff.
|
| - connecting_ = false;
|
| - backoff_reset_time_ = NowTicks();
|
| - previous_backoff_.swap(backoff_entry_);
|
| - backoff_entry_->Reset();
|
| + DCHECK(!connecting_);
|
| + if (result != net::OK) {
|
| + // TODO(zea): Consider how to handle errors that may require some sort of
|
| + // user intervention (login page, etc.).
|
| + UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionDisconnectErrorCode", result);
|
| + SignalConnectionReset(SOCKET_FAILURE);
|
| return;
|
| }
|
|
|
| - if (!connecting_)
|
| - UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionDisconnectErrorCode", result);
|
| -
|
| - if (connection_handler_)
|
| - connection_handler_->Reset();
|
| -
|
| - // TODO(zea): Consider how to handle errors that may require some sort of
|
| - // user intervention (login page, etc.).
|
| - LOG(ERROR) << "Connection reset with error " << result;
|
| - backoff_entry_->InformOfRequest(false);
|
| - Connect();
|
| + // Handshake complete, reset backoff. If the login failed with an error,
|
| + // the client should invoke SignalConnectionReset(LOGIN_FAILURE), which will
|
| + // restore the previous backoff.
|
| + last_login_time_ = NowTicks();
|
| + previous_backoff_.swap(backoff_entry_);
|
| + backoff_entry_->Reset();
|
| + logging_in_ = false;
|
| }
|
|
|
| } // namespace gcm
|
|
|