Chromium Code Reviews| 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 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |