OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "google_apis/gcm/engine/connection_factory_impl.h" | 5 #include "google_apis/gcm/engine/connection_factory_impl.h" |
6 | 6 |
7 #include "base/message_loop/message_loop.h" | 7 #include "base/message_loop/message_loop.h" |
8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
9 #include "base/metrics/sparse_histogram.h" | 9 #include "base/metrics/sparse_histogram.h" |
10 #include "google_apis/gcm/engine/connection_handler_impl.h" | 10 #include "google_apis/gcm/engine/connection_handler_impl.h" |
(...skipping 11 matching lines...) Expand all Loading... |
22 namespace { | 22 namespace { |
23 | 23 |
24 // The amount of time a Socket read should wait before timing out. | 24 // The amount of time a Socket read should wait before timing out. |
25 const int kReadTimeoutMs = 30000; // 30 seconds. | 25 const int kReadTimeoutMs = 30000; // 30 seconds. |
26 | 26 |
27 // If a connection is reset after succeeding within this window of time, | 27 // If a connection is reset after succeeding within this window of time, |
28 // the previous backoff entry is restored (and the connection success is treated | 28 // the previous backoff entry is restored (and the connection success is treated |
29 // as if it was transient). | 29 // as if it was transient). |
30 const int kConnectionResetWindowSecs = 10; // 10 seconds. | 30 const int kConnectionResetWindowSecs = 10; // 10 seconds. |
31 | 31 |
| 32 // Decides whether the last login was within kConnectionResetWindowSecs of now |
| 33 // or not. |
| 34 bool ShouldRestorePreviousBackoff(const base::TimeTicks& login_time, |
| 35 const base::TimeTicks& now_ticks) { |
| 36 return !login_time.is_null() && |
| 37 now_ticks - login_time <= |
| 38 base::TimeDelta::FromSeconds(kConnectionResetWindowSecs); |
| 39 } |
| 40 |
32 } // namespace | 41 } // namespace |
33 | 42 |
34 ConnectionFactoryImpl::ConnectionFactoryImpl( | 43 ConnectionFactoryImpl::ConnectionFactoryImpl( |
35 const GURL& mcs_endpoint, | 44 const GURL& mcs_endpoint, |
36 const net::BackoffEntry::Policy& backoff_policy, | 45 const net::BackoffEntry::Policy& backoff_policy, |
37 scoped_refptr<net::HttpNetworkSession> network_session, | 46 scoped_refptr<net::HttpNetworkSession> network_session, |
38 net::NetLog* net_log) | 47 net::NetLog* net_log) |
39 : mcs_endpoint_(mcs_endpoint), | 48 : mcs_endpoint_(mcs_endpoint), |
40 backoff_policy_(backoff_policy), | 49 backoff_policy_(backoff_policy), |
41 network_session_(network_session), | 50 network_session_(network_session), |
42 net_log_(net_log), | 51 net_log_(net_log), |
43 connecting_(false), | 52 connecting_(false), |
| 53 logging_in_(false), |
44 weak_ptr_factory_(this) { | 54 weak_ptr_factory_(this) { |
45 } | 55 } |
46 | 56 |
47 ConnectionFactoryImpl::~ConnectionFactoryImpl() { | 57 ConnectionFactoryImpl::~ConnectionFactoryImpl() { |
48 } | 58 } |
49 | 59 |
50 void ConnectionFactoryImpl::Initialize( | 60 void ConnectionFactoryImpl::Initialize( |
51 const BuildLoginRequestCallback& request_builder, | 61 const BuildLoginRequestCallback& request_builder, |
52 const ConnectionHandler::ProtoReceivedCallback& read_callback, | 62 const ConnectionHandler::ProtoReceivedCallback& read_callback, |
53 const ConnectionHandler::ProtoSentCallback& write_callback) { | 63 const ConnectionHandler::ProtoSentCallback& write_callback) { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
91 DVLOG(1) << "Attempting connection to MCS endpoint."; | 101 DVLOG(1) << "Attempting connection to MCS endpoint."; |
92 ConnectImpl(); | 102 ConnectImpl(); |
93 } | 103 } |
94 | 104 |
95 bool ConnectionFactoryImpl::IsEndpointReachable() const { | 105 bool ConnectionFactoryImpl::IsEndpointReachable() const { |
96 return connection_handler_ && | 106 return connection_handler_ && |
97 connection_handler_->CanSendMessage() && | 107 connection_handler_->CanSendMessage() && |
98 !connecting_; | 108 !connecting_; |
99 } | 109 } |
100 | 110 |
101 void ConnectionFactoryImpl::SignalConnectionReset() { | 111 void ConnectionFactoryImpl::SignalConnectionReset( |
| 112 ConnectionResetReason reason) { |
| 113 // A failure can trigger multiple resets, so no need to do anything if a |
| 114 // connection is already in progress. |
102 if (connecting_) | 115 if (connecting_) |
103 return; // Already attempting to reconnect. | 116 return; |
| 117 |
| 118 UMA_HISTOGRAM_ENUMERATION("GCM.ConnectionResetReason", |
| 119 reason, |
| 120 CONNECTION_RESET_COUNT); |
| 121 if (!last_login_time_.is_null()) { |
| 122 UMA_HISTOGRAM_CUSTOM_TIMES("GCM.ConnectionUpTime", |
| 123 NowTicks() - last_login_time_, |
| 124 base::TimeDelta::FromSeconds(1), |
| 125 base::TimeDelta::FromHours(24), |
| 126 50); |
| 127 // |last_login_time_| will be reset below, before attempting the new |
| 128 // connection. |
| 129 } |
104 | 130 |
105 if (connection_handler_) | 131 if (connection_handler_) |
106 connection_handler_->Reset(); | 132 connection_handler_->Reset(); |
107 | 133 |
108 if (!backoff_reset_time_.is_null() && | 134 if (socket_handle_.socket() && socket_handle_.socket()->IsConnected()) |
109 NowTicks() - backoff_reset_time_ <= | 135 socket_handle_.socket()->Disconnect(); |
110 base::TimeDelta::FromSeconds(kConnectionResetWindowSecs)) { | 136 socket_handle_.Reset(); |
| 137 |
| 138 if (logging_in_) { |
| 139 // Failures prior to login completion just reuse the existing backoff entry. |
| 140 logging_in_ = false; |
| 141 backoff_entry_->InformOfRequest(false); |
| 142 } else if (reason == LOGIN_FAILURE || |
| 143 ShouldRestorePreviousBackoff(last_login_time_, NowTicks())) { |
| 144 // Failures due to login, or within the reset window of a login, restore |
| 145 // the backoff entry that was saved off at login completion time. |
111 backoff_entry_.swap(previous_backoff_); | 146 backoff_entry_.swap(previous_backoff_); |
112 backoff_entry_->InformOfRequest(false); | 147 backoff_entry_->InformOfRequest(false); |
| 148 } else { |
| 149 // We shouldn't be in backoff in thise case. |
| 150 DCHECK(backoff_entry_->CanDiscard()); |
113 } | 151 } |
114 backoff_reset_time_ = base::TimeTicks(); | 152 |
115 previous_backoff_->Reset(); | 153 // At this point the last login time has been consumed or deemed irrelevant, |
| 154 // reset it. |
| 155 last_login_time_ = base::TimeTicks(); |
| 156 |
116 Connect(); | 157 Connect(); |
117 } | 158 } |
118 | 159 |
119 base::TimeTicks ConnectionFactoryImpl::NextRetryAttempt() const { | 160 base::TimeTicks ConnectionFactoryImpl::NextRetryAttempt() const { |
120 if (!backoff_entry_) | 161 if (!backoff_entry_) |
121 return base::TimeTicks(); | 162 return base::TimeTicks(); |
122 return backoff_entry_->GetReleaseTime(); | 163 return backoff_entry_->GetReleaseTime(); |
123 } | 164 } |
124 | 165 |
125 void ConnectionFactoryImpl::OnConnectionTypeChanged( | 166 void ConnectionFactoryImpl::OnConnectionTypeChanged( |
(...skipping 10 matching lines...) Expand all Loading... |
136 } | 177 } |
137 | 178 |
138 void ConnectionFactoryImpl::OnIPAddressChanged() { | 179 void ConnectionFactoryImpl::OnIPAddressChanged() { |
139 DVLOG(1) << "IP Address changed, resetting backoff."; | 180 DVLOG(1) << "IP Address changed, resetting backoff."; |
140 backoff_entry_->Reset(); | 181 backoff_entry_->Reset(); |
141 // Connect(..) should be retrying with backoff already if a connection is | 182 // Connect(..) should be retrying with backoff already if a connection is |
142 // necessary, so no need to call again. | 183 // necessary, so no need to call again. |
143 } | 184 } |
144 | 185 |
145 void ConnectionFactoryImpl::ConnectImpl() { | 186 void ConnectionFactoryImpl::ConnectImpl() { |
146 DCHECK(!connection_handler_->CanSendMessage()); | 187 DCHECK(connecting_); |
147 if (socket_handle_.socket() && socket_handle_.socket()->IsConnected()) | 188 DCHECK(!socket_handle_.socket()); |
148 socket_handle_.socket()->Disconnect(); | |
149 socket_handle_.Reset(); | |
150 | 189 |
151 // TODO(zea): resolve proxies. | 190 // TODO(zea): resolve proxies. |
152 net::ProxyInfo proxy_info; | 191 net::ProxyInfo proxy_info; |
153 proxy_info.UseDirect(); | 192 proxy_info.UseDirect(); |
154 net::SSLConfig ssl_config; | 193 net::SSLConfig ssl_config; |
155 network_session_->ssl_config_service()->GetSSLConfig(&ssl_config); | 194 network_session_->ssl_config_service()->GetSSLConfig(&ssl_config); |
156 | 195 |
157 int status = net::InitSocketHandleForTlsConnect( | 196 int status = net::InitSocketHandleForTlsConnect( |
158 net::HostPortPair::FromURL(mcs_endpoint_), | 197 net::HostPortPair::FromURL(mcs_endpoint_), |
159 network_session_.get(), | 198 network_session_.get(), |
(...skipping 23 matching lines...) Expand all Loading... |
183 scoped_ptr<net::BackoffEntry> ConnectionFactoryImpl::CreateBackoffEntry( | 222 scoped_ptr<net::BackoffEntry> ConnectionFactoryImpl::CreateBackoffEntry( |
184 const net::BackoffEntry::Policy* const policy) { | 223 const net::BackoffEntry::Policy* const policy) { |
185 return scoped_ptr<net::BackoffEntry>(new net::BackoffEntry(policy)); | 224 return scoped_ptr<net::BackoffEntry>(new net::BackoffEntry(policy)); |
186 } | 225 } |
187 | 226 |
188 base::TimeTicks ConnectionFactoryImpl::NowTicks() { | 227 base::TimeTicks ConnectionFactoryImpl::NowTicks() { |
189 return base::TimeTicks::Now(); | 228 return base::TimeTicks::Now(); |
190 } | 229 } |
191 | 230 |
192 void ConnectionFactoryImpl::OnConnectDone(int result) { | 231 void ConnectionFactoryImpl::OnConnectDone(int result) { |
| 232 UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", (result == net::OK)); |
| 233 |
193 if (result != net::OK) { | 234 if (result != net::OK) { |
194 LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result; | 235 LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result; |
195 backoff_entry_->InformOfRequest(false); | 236 backoff_entry_->InformOfRequest(false); |
196 UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionFailureErrorCode", result); | 237 UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionFailureErrorCode", result); |
197 Connect(); | 238 Connect(); |
198 return; | 239 return; |
199 } | 240 } |
200 | 241 |
201 DVLOG(1) << "MCS endpoint socket connection success, starting handshake."; | 242 connecting_ = false; |
| 243 logging_in_ = true; |
| 244 DVLOG(1) << "MCS endpoint socket connection success, starting login."; |
202 InitHandler(); | 245 InitHandler(); |
203 } | 246 } |
204 | 247 |
205 void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) { | 248 void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) { |
206 if (result == net::OK) { | 249 DCHECK(!connecting_); |
207 // Handshake succeeded, reset the backoff. | 250 if (result != net::OK) { |
208 connecting_ = false; | 251 // TODO(zea): Consider how to handle errors that may require some sort of |
209 backoff_reset_time_ = NowTicks(); | 252 // user intervention (login page, etc.). |
210 previous_backoff_.swap(backoff_entry_); | 253 UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionDisconnectErrorCode", result); |
211 backoff_entry_->Reset(); | 254 SignalConnectionReset(SOCKET_FAILURE); |
212 return; | 255 return; |
213 } | 256 } |
214 | 257 |
215 if (!connecting_) | 258 // Handshake complete, reset backoff. If the login failed with an error, |
216 UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionDisconnectErrorCode", result); | 259 // the client should invoke SignalConnectionReset(LOGIN_FAILURE), which will |
217 | 260 // restore the previous backoff. |
218 if (connection_handler_) | 261 last_login_time_ = NowTicks(); |
219 connection_handler_->Reset(); | 262 previous_backoff_.swap(backoff_entry_); |
220 | 263 backoff_entry_->Reset(); |
221 // TODO(zea): Consider how to handle errors that may require some sort of | 264 logging_in_ = false; |
222 // user intervention (login page, etc.). | |
223 LOG(ERROR) << "Connection reset with error " << result; | |
224 backoff_entry_->InformOfRequest(false); | |
225 Connect(); | |
226 } | 265 } |
227 | 266 |
228 } // namespace gcm | 267 } // namespace gcm |
OLD | NEW |