Chromium Code Reviews| 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", |
|
fgorski
2014/02/12 19:24:01
Perhaps we could wrap that in LogConnectionResetRe
Nicolas Zea
2014/02/12 21:50:16
I think that these methods are already the ones th
fgorski
2014/02/12 21:56:27
OK, I guess we can figure it out when we are close
|
| + 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 |