Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: google_apis/gcm/engine/connection_factory_impl.cc

Issue 213693002: [GCM] Add support for canary connection attempts (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Refactor tests Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 net::NetLog* net_log) 47 net::NetLog* net_log)
48 : mcs_endpoints_(mcs_endpoints), 48 : mcs_endpoints_(mcs_endpoints),
49 next_endpoint_(0), 49 next_endpoint_(0),
50 last_successful_endpoint_(0), 50 last_successful_endpoint_(0),
51 backoff_policy_(backoff_policy), 51 backoff_policy_(backoff_policy),
52 network_session_(network_session), 52 network_session_(network_session),
53 bound_net_log_( 53 bound_net_log_(
54 net::BoundNetLog::Make(net_log, net::NetLog::SOURCE_SOCKET)), 54 net::BoundNetLog::Make(net_log, net::NetLog::SOURCE_SOCKET)),
55 pac_request_(NULL), 55 pac_request_(NULL),
56 connecting_(false), 56 connecting_(false),
57 waiting_for_backoff_(false),
57 logging_in_(false), 58 logging_in_(false),
58 weak_ptr_factory_(this) { 59 weak_ptr_factory_(this) {
59 DCHECK_GE(mcs_endpoints_.size(), 1U); 60 DCHECK_GE(mcs_endpoints_.size(), 1U);
60 } 61 }
61 62
62 ConnectionFactoryImpl::~ConnectionFactoryImpl() { 63 ConnectionFactoryImpl::~ConnectionFactoryImpl() {
63 if (pac_request_) { 64 if (pac_request_) {
64 network_session_->proxy_service()->CancelPacRequest(pac_request_); 65 network_session_->proxy_service()->CancelPacRequest(pac_request_);
65 pac_request_ = NULL; 66 pac_request_ = NULL;
66 } 67 }
67 } 68 }
68 69
69 void ConnectionFactoryImpl::Initialize( 70 void ConnectionFactoryImpl::Initialize(
70 const BuildLoginRequestCallback& request_builder, 71 const BuildLoginRequestCallback& request_builder,
71 const ConnectionHandler::ProtoReceivedCallback& read_callback, 72 const ConnectionHandler::ProtoReceivedCallback& read_callback,
72 const ConnectionHandler::ProtoSentCallback& write_callback) { 73 const ConnectionHandler::ProtoSentCallback& write_callback) {
73 DCHECK(!connection_handler_); 74 DCHECK(!connection_handler_);
74 75
75 previous_backoff_ = CreateBackoffEntry(&backoff_policy_); 76 previous_backoff_ = CreateBackoffEntry(&backoff_policy_);
76 backoff_entry_ = CreateBackoffEntry(&backoff_policy_); 77 backoff_entry_ = CreateBackoffEntry(&backoff_policy_);
77 request_builder_ = request_builder; 78 request_builder_ = request_builder;
78 79
79 net::NetworkChangeNotifier::AddIPAddressObserver(this); 80 net::NetworkChangeNotifier::AddIPAddressObserver(this);
80 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); 81 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
81 connection_handler_.reset( 82 connection_handler_ = CreateConnectionHandler(
82 new ConnectionHandlerImpl( 83 base::TimeDelta::FromMilliseconds(kReadTimeoutMs),
83 base::TimeDelta::FromMilliseconds(kReadTimeoutMs), 84 read_callback,
84 read_callback, 85 write_callback,
85 write_callback, 86 base::Bind(&ConnectionFactoryImpl::ConnectionHandlerCallback,
86 base::Bind(&ConnectionFactoryImpl::ConnectionHandlerCallback, 87 weak_ptr_factory_.GetWeakPtr())).Pass();
87 weak_ptr_factory_.GetWeakPtr())));
88 } 88 }
89 89
90 ConnectionHandler* ConnectionFactoryImpl::GetConnectionHandler() const { 90 ConnectionHandler* ConnectionFactoryImpl::GetConnectionHandler() const {
91 return connection_handler_.get(); 91 return connection_handler_.get();
92 } 92 }
93 93
94 void ConnectionFactoryImpl::Connect() { 94 void ConnectionFactoryImpl::Connect() {
95 DCHECK(connection_handler_); 95 DCHECK(connection_handler_);
96 96
97 connecting_ = true; 97 if (connecting_ || waiting_for_backoff_)
jianli 2014/03/28 04:26:13 Now it becomes a bit harder to follow all the logi
Nicolas Zea 2014/03/28 17:53:42 The problem is that, because of canaries, these tw
98 return; // Connection attempt already in progress or pending.
99
100 if (IsEndpointReachable())
101 return; // Already connected.
102
103 ConnectWithBackoff();
104 }
105
106 void ConnectionFactoryImpl::ConnectWithBackoff() {
107 // If a canary managed to connect while a backoff expiration was pending,
108 // just cleanup the internal state.
109 if (connecting_ || IsEndpointReachable()) {
110 waiting_for_backoff_ = false;
111 return;
112 }
113
98 if (backoff_entry_->ShouldRejectRequest()) { 114 if (backoff_entry_->ShouldRejectRequest()) {
99 DVLOG(1) << "Delaying MCS endpoint connection for " 115 DVLOG(1) << "Delaying MCS endpoint connection for "
100 << backoff_entry_->GetTimeUntilRelease().InMilliseconds() 116 << backoff_entry_->GetTimeUntilRelease().InMilliseconds()
101 << " milliseconds."; 117 << " milliseconds.";
118 waiting_for_backoff_ = true;
102 base::MessageLoop::current()->PostDelayedTask( 119 base::MessageLoop::current()->PostDelayedTask(
103 FROM_HERE, 120 FROM_HERE,
104 base::Bind(&ConnectionFactoryImpl::Connect, 121 base::Bind(&ConnectionFactoryImpl::ConnectWithBackoff,
105 weak_ptr_factory_.GetWeakPtr()), 122 weak_ptr_factory_.GetWeakPtr()),
106 backoff_entry_->GetTimeUntilRelease()); 123 backoff_entry_->GetTimeUntilRelease());
107 return; 124 return;
108 } 125 }
109 126
110 DVLOG(1) << "Attempting connection to MCS endpoint."; 127 DVLOG(1) << "Attempting connection to MCS endpoint.";
128 waiting_for_backoff_ = false;
111 ConnectImpl(); 129 ConnectImpl();
112 } 130 }
113 131
114 bool ConnectionFactoryImpl::IsEndpointReachable() const { 132 bool ConnectionFactoryImpl::IsEndpointReachable() const {
115 return connection_handler_ && 133 return connection_handler_ && connection_handler_->CanSendMessage();
116 connection_handler_->CanSendMessage() &&
117 !connecting_;
118 } 134 }
119 135
120 void ConnectionFactoryImpl::SignalConnectionReset( 136 void ConnectionFactoryImpl::SignalConnectionReset(
121 ConnectionResetReason reason) { 137 ConnectionResetReason reason) {
122 // A failure can trigger multiple resets, so no need to do anything if a 138 // A failure can trigger multiple resets, so no need to do anything if a
123 // connection is already in progress. 139 // connection is already in progress.
124 if (connecting_) 140 if (connecting_) {
141 DVLOG(1) << "Connection in progress, ignoring reset.";
125 return; 142 return;
143 }
126 144
127 UMA_HISTOGRAM_ENUMERATION("GCM.ConnectionResetReason", 145 UMA_HISTOGRAM_ENUMERATION("GCM.ConnectionResetReason",
128 reason, 146 reason,
129 CONNECTION_RESET_COUNT); 147 CONNECTION_RESET_COUNT);
130 if (!last_login_time_.is_null()) { 148 if (!last_login_time_.is_null()) {
131 UMA_HISTOGRAM_CUSTOM_TIMES("GCM.ConnectionUpTime", 149 UMA_HISTOGRAM_CUSTOM_TIMES("GCM.ConnectionUpTime",
132 NowTicks() - last_login_time_, 150 NowTicks() - last_login_time_,
133 base::TimeDelta::FromSeconds(1), 151 base::TimeDelta::FromSeconds(1),
134 base::TimeDelta::FromHours(24), 152 base::TimeDelta::FromHours(24),
135 50); 153 50);
136 // |last_login_time_| will be reset below, before attempting the new 154 // |last_login_time_| will be reset below, before attempting the new
137 // connection. 155 // connection.
138 } 156 }
139 157
140 CloseSocket(); 158 CloseSocket();
159 DCHECK(!IsEndpointReachable());
160
161 // Network changes get special treatment as they can trigger a one-off canary
162 // request that bypasses backoff (but does nothing if a connection is in
163 // progress). Other connection reset events can be ignored as a connection
164 // is already awaiting backoff expiration.
165 if (waiting_for_backoff_ && reason != NETWORK_CHANGE) {
166 DVLOG(1) << "Backoff expiration pending, ignoring reset.";
167 return;
168 }
141 169
142 if (logging_in_) { 170 if (logging_in_) {
143 // Failures prior to login completion just reuse the existing backoff entry. 171 // Failures prior to login completion just reuse the existing backoff entry.
144 logging_in_ = false; 172 logging_in_ = false;
145 backoff_entry_->InformOfRequest(false); 173 backoff_entry_->InformOfRequest(false);
146 } else if (reason == LOGIN_FAILURE || 174 } else if (reason == LOGIN_FAILURE ||
147 ShouldRestorePreviousBackoff(last_login_time_, NowTicks())) { 175 ShouldRestorePreviousBackoff(last_login_time_, NowTicks())) {
148 // Failures due to login, or within the reset window of a login, restore 176 // Failures due to login, or within the reset window of a login, restore
149 // the backoff entry that was saved off at login completion time. 177 // the backoff entry that was saved off at login completion time.
150 backoff_entry_.swap(previous_backoff_); 178 backoff_entry_.swap(previous_backoff_);
151 backoff_entry_->InformOfRequest(false); 179 backoff_entry_->InformOfRequest(false);
180 } else if (reason == NETWORK_CHANGE) {
181 ConnectImpl(); // Canary attempts bypass backoff without resetting it.
182 return;
152 } else { 183 } else {
153 // We shouldn't be in backoff in thise case. 184 // We shouldn't be in backoff in thise case.
154 DCHECK_EQ(0, backoff_entry_->failure_count()); 185 DCHECK_EQ(0, backoff_entry_->failure_count());
155 } 186 }
187 DCHECK(!connecting_);
188 DCHECK(!waiting_for_backoff_);
156 189
157 // At this point the last login time has been consumed or deemed irrelevant, 190 // At this point the last login time has been consumed or deemed irrelevant,
158 // reset it. 191 // reset it.
159 last_login_time_ = base::TimeTicks(); 192 last_login_time_ = base::TimeTicks();
160 193
161 Connect(); 194 Connect();
162 } 195 }
163 196
164 base::TimeTicks ConnectionFactoryImpl::NextRetryAttempt() const { 197 base::TimeTicks ConnectionFactoryImpl::NextRetryAttempt() const {
165 if (!backoff_entry_) 198 if (!backoff_entry_)
166 return base::TimeTicks(); 199 return base::TimeTicks();
167 return backoff_entry_->GetReleaseTime(); 200 return backoff_entry_->GetReleaseTime();
168 } 201 }
169 202
170 void ConnectionFactoryImpl::OnConnectionTypeChanged( 203 void ConnectionFactoryImpl::OnConnectionTypeChanged(
171 net::NetworkChangeNotifier::ConnectionType type) { 204 net::NetworkChangeNotifier::ConnectionType type) {
172 if (type == net::NetworkChangeNotifier::CONNECTION_NONE) 205 if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
173 return; 206 return;
174 207
175 // TODO(zea): implement different backoff/retry policies based on connection 208 DVLOG(1) << "Connection type changed to " << type << ", reconnecting.";
176 // type. 209
177 DVLOG(1) << "Connection type changed to " << type << ", resetting backoff."; 210 // The connection may have been silently dropped, attempt to reconnect.
178 backoff_entry_->Reset(); 211 SignalConnectionReset(NETWORK_CHANGE);
179 // Connect(..) should be retrying with backoff already if a connection is
180 // necessary, so no need to call again.
181 } 212 }
182 213
183 void ConnectionFactoryImpl::OnIPAddressChanged() { 214 void ConnectionFactoryImpl::OnIPAddressChanged() {
184 DVLOG(1) << "IP Address changed, resetting backoff."; 215 DVLOG(1) << "IP Address changed, reconnecting.";
185 backoff_entry_->Reset(); 216
186 // Connect(..) should be retrying with backoff already if a connection is 217 // The connection may have been silently dropped, attempt to reconnect.
187 // necessary, so no need to call again. 218 SignalConnectionReset(NETWORK_CHANGE);
188 } 219 }
189 220
190 GURL ConnectionFactoryImpl::GetCurrentEndpoint() const { 221 GURL ConnectionFactoryImpl::GetCurrentEndpoint() const {
191 // Note that IsEndpointReachable() returns false anytime connecting_ is true, 222 // Note that IsEndpointReachable() returns false anytime connecting_ is true,
192 // so while connecting this always uses |next_endpoint_|. 223 // so while connecting this always uses |next_endpoint_|.
193 if (IsEndpointReachable()) 224 if (IsEndpointReachable())
194 return mcs_endpoints_[last_successful_endpoint_]; 225 return mcs_endpoints_[last_successful_endpoint_];
195 return mcs_endpoints_[next_endpoint_]; 226 return mcs_endpoints_[next_endpoint_];
196 } 227 }
197 228
198 void ConnectionFactoryImpl::ConnectImpl() { 229 void ConnectionFactoryImpl::ConnectImpl() {
199 DCHECK(connecting_); 230 DCHECK(!IsEndpointReachable());
200 DCHECK(!socket_handle_.socket()); 231 DCHECK(!socket_handle_.socket());
201 232
233 connecting_ = true;
202 int status = network_session_->proxy_service()->ResolveProxy( 234 int status = network_session_->proxy_service()->ResolveProxy(
203 GetCurrentEndpoint(), 235 GetCurrentEndpoint(),
204 &proxy_info_, 236 &proxy_info_,
205 base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone, 237 base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
206 weak_ptr_factory_.GetWeakPtr()), 238 weak_ptr_factory_.GetWeakPtr()),
207 &pac_request_, 239 &pac_request_,
208 bound_net_log_); 240 bound_net_log_);
209 if (status != net::ERR_IO_PENDING) 241 if (status != net::ERR_IO_PENDING)
210 OnProxyResolveDone(status); 242 OnProxyResolveDone(status);
211 } 243 }
212 244
213 void ConnectionFactoryImpl::InitHandler() { 245 void ConnectionFactoryImpl::InitHandler() {
214 // May be null in tests. 246 // May be null in tests.
215 mcs_proto::LoginRequest login_request; 247 mcs_proto::LoginRequest login_request;
216 if (!request_builder_.is_null()) { 248 if (!request_builder_.is_null()) {
217 request_builder_.Run(&login_request); 249 request_builder_.Run(&login_request);
218 DCHECK(login_request.IsInitialized()); 250 DCHECK(login_request.IsInitialized());
219 } 251 }
220 252
221 connection_handler_->Init(login_request, socket_handle_.socket()); 253 connection_handler_->Init(login_request, socket_handle_.socket());
222 } 254 }
223 255
224 scoped_ptr<net::BackoffEntry> ConnectionFactoryImpl::CreateBackoffEntry( 256 scoped_ptr<net::BackoffEntry> ConnectionFactoryImpl::CreateBackoffEntry(
225 const net::BackoffEntry::Policy* const policy) { 257 const net::BackoffEntry::Policy* const policy) {
226 return scoped_ptr<net::BackoffEntry>(new net::BackoffEntry(policy)); 258 return scoped_ptr<net::BackoffEntry>(new net::BackoffEntry(policy));
227 } 259 }
228 260
261 scoped_ptr<ConnectionHandler> ConnectionFactoryImpl::CreateConnectionHandler(
262 base::TimeDelta read_timeout,
263 const ConnectionHandler::ProtoReceivedCallback& read_callback,
264 const ConnectionHandler::ProtoSentCallback& write_callback,
265 const ConnectionHandler::ConnectionChangedCallback& connection_callback) {
266 return make_scoped_ptr<ConnectionHandler>(
267 new ConnectionHandlerImpl(read_timeout,
268 read_callback,
269 write_callback,
270 connection_callback));
271 }
272
229 base::TimeTicks ConnectionFactoryImpl::NowTicks() { 273 base::TimeTicks ConnectionFactoryImpl::NowTicks() {
230 return base::TimeTicks::Now(); 274 return base::TimeTicks::Now();
231 } 275 }
232 276
233 void ConnectionFactoryImpl::OnConnectDone(int result) { 277 void ConnectionFactoryImpl::OnConnectDone(int result) {
234 if (result != net::OK) { 278 if (result != net::OK) {
235 // If the connection fails, try another proxy. 279 // If the connection fails, try another proxy.
236 result = ReconsiderProxyAfterError(result); 280 result = ReconsiderProxyAfterError(result);
237 // ReconsiderProxyAfterError either returns an error (in which case it is 281 // ReconsiderProxyAfterError either returns an error (in which case it is
238 // not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering 282 // not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering
239 // another proxy. 283 // another proxy.
240 DCHECK_NE(result, net::OK); 284 DCHECK_NE(result, net::OK);
241 if (result == net::ERR_IO_PENDING) 285 if (result == net::ERR_IO_PENDING)
242 return; // Proxy reconsideration pending. Return. 286 return; // Proxy reconsideration pending. Return.
243 LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result; 287 LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result;
244 UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", false); 288 UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", false);
245 CloseSocket(); 289 CloseSocket();
246 backoff_entry_->InformOfRequest(false); 290 backoff_entry_->InformOfRequest(false);
247 UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionFailureErrorCode", result); 291 UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionFailureErrorCode", result);
248 292
249 // If there are other endpoints available, use the next endpoint on the 293 // If there are other endpoints available, use the next endpoint on the
250 // subsequent retry. 294 // subsequent retry.
251 next_endpoint_++; 295 next_endpoint_++;
252 if (next_endpoint_ >= mcs_endpoints_.size()) 296 if (next_endpoint_ >= mcs_endpoints_.size())
253 next_endpoint_ = 0; 297 next_endpoint_ = 0;
298 connecting_ = false;
254 Connect(); 299 Connect();
255 return; 300 return;
256 } 301 }
257 302
258 UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", true); 303 UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", true);
259 UMA_HISTOGRAM_COUNTS("GCM.ConnectionEndpoint", next_endpoint_); 304 UMA_HISTOGRAM_COUNTS("GCM.ConnectionEndpoint", next_endpoint_);
260 UMA_HISTOGRAM_BOOLEAN("GCM.ConnectedViaProxy", 305 UMA_HISTOGRAM_BOOLEAN("GCM.ConnectedViaProxy",
261 !(proxy_info_.is_empty() || proxy_info_.is_direct())); 306 !(proxy_info_.is_empty() || proxy_info_.is_direct()));
262 ReportSuccessfulProxyConnection(); 307 ReportSuccessfulProxyConnection();
263 308
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
436 // the destroyed socket. 481 // the destroyed socket.
437 if (connection_handler_) 482 if (connection_handler_)
438 connection_handler_->Reset(); 483 connection_handler_->Reset();
439 484
440 if (socket_handle_.socket() && socket_handle_.socket()->IsConnected()) 485 if (socket_handle_.socket() && socket_handle_.socket()->IsConnected())
441 socket_handle_.socket()->Disconnect(); 486 socket_handle_.socket()->Disconnect();
442 socket_handle_.Reset(); 487 socket_handle_.Reset();
443 } 488 }
444 489
445 } // namespace gcm 490 } // namespace gcm
OLDNEW
« no previous file with comments | « google_apis/gcm/engine/connection_factory_impl.h ('k') | google_apis/gcm/engine/connection_factory_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698