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
3 // found in the LICENSE file.
5 #include "google_apis/gcm/engine/connection_factory_impl.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/metrics/histogram.h"
9 #include "base/metrics/sparse_histogram.h"
10 #include "google_apis/gcm/engine/connection_handler_impl.h"
11 #include "google_apis/gcm/protocol/mcs.pb.h"
12 #include "net/base/net_errors.h"
13 #include "net/http/http_network_session.h"
14 #include "net/http/http_request_headers.h"
15 #include "net/proxy/proxy_info.h"
16 #include "net/socket/client_socket_handle.h"
17 #include "net/socket/client_socket_pool_manager.h"
18 #include "net/ssl/ssl_config_service.h"
24 // The amount of time a Socket read should wait before timing out.
25 const int kReadTimeoutMs = 30000; // 30 seconds.
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
29 // as if it was transient).
30 const int kConnectionResetWindowSecs = 10; // 10 seconds.
33 const net::BackoffEntry::Policy kConnectionBackoffPolicy = {
34 // Number of initial errors (in sequence) to ignore before applying
35 // exponential back-off rules.
38 // Initial delay for exponential back-off in ms.
41 // Factor by which the waiting time will be multiplied.
44 // Fuzzing percentage. ex: 10% will spread requests randomly
45 // between 90%-100% of the calculated time.
48 // Maximum amount of time we are willing to delay our request in ms.
49 1000 * 60 * 5, // 5 minutes.
51 // Time to keep an entry from being discarded even when it
52 // has no significant state, -1 to never discard.
55 // Don't use initial delay unless the last request was an error.
61 ConnectionFactoryImpl::ConnectionFactoryImpl(
62 const GURL& mcs_endpoint,
63 scoped_refptr<net::HttpNetworkSession> network_session,
65 : mcs_endpoint_(mcs_endpoint),
66 network_session_(network_session),
69 weak_ptr_factory_(this) {
72 ConnectionFactoryImpl::~ConnectionFactoryImpl() {
75 void ConnectionFactoryImpl::Initialize(
76 const BuildLoginRequestCallback& request_builder,
77 const ConnectionHandler::ProtoReceivedCallback& read_callback,
78 const ConnectionHandler::ProtoSentCallback& write_callback) {
79 DCHECK(!connection_handler_);
81 previous_backoff_ = CreateBackoffEntry(&kConnectionBackoffPolicy);
82 backoff_entry_ = CreateBackoffEntry(&kConnectionBackoffPolicy);
83 request_builder_ = request_builder;
85 net::NetworkChangeNotifier::AddIPAddressObserver(this);
86 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
87 connection_handler_.reset(
88 new ConnectionHandlerImpl(
89 base::TimeDelta::FromMilliseconds(kReadTimeoutMs),
92 base::Bind(&ConnectionFactoryImpl::ConnectionHandlerCallback,
93 weak_ptr_factory_.GetWeakPtr())));
96 ConnectionHandler* ConnectionFactoryImpl::GetConnectionHandler() const {
97 return connection_handler_.get();
100 void ConnectionFactoryImpl::Connect() {
101 DCHECK(connection_handler_);
104 if (backoff_entry_->ShouldRejectRequest()) {
105 DVLOG(1) << "Delaying MCS endpoint connection for "
106 << backoff_entry_->GetTimeUntilRelease().InMilliseconds()
108 base::MessageLoop::current()->PostDelayedTask(
110 base::Bind(&ConnectionFactoryImpl::Connect,
111 weak_ptr_factory_.GetWeakPtr()),
112 backoff_entry_->GetTimeUntilRelease());
116 DVLOG(1) << "Attempting connection to MCS endpoint.";
120 bool ConnectionFactoryImpl::IsEndpointReachable() const {
121 return connection_handler_ &&
122 connection_handler_->CanSendMessage() &&
126 void ConnectionFactoryImpl::SignalConnectionReset() {
128 return; // Already attempting to reconnect.
130 if (!backoff_reset_time_.is_null() &&
131 NowTicks() - backoff_reset_time_ <=
132 base::TimeDelta::FromSeconds(kConnectionResetWindowSecs)) {
133 backoff_entry_.swap(previous_backoff_);
134 backoff_entry_->InformOfRequest(false);
136 backoff_reset_time_ = base::TimeTicks();
137 previous_backoff_->Reset();
141 base::TimeTicks ConnectionFactoryImpl::NextRetryAttempt() const {
143 return base::TimeTicks();
144 return backoff_entry_->GetReleaseTime();
147 void ConnectionFactoryImpl::OnConnectionTypeChanged(
148 net::NetworkChangeNotifier::ConnectionType type) {
149 if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
152 // TODO(zea): implement different backoff/retry policies based on connection
154 DVLOG(1) << "Connection type changed to " << type << ", resetting backoff.";
155 backoff_entry_->Reset();
156 // Connect(..) should be retrying with backoff already if a connection is
157 // necessary, so no need to call again.
160 void ConnectionFactoryImpl::OnIPAddressChanged() {
161 DVLOG(1) << "IP Address changed, resetting backoff.";
162 backoff_entry_->Reset();
163 // Connect(..) should be retrying with backoff already if a connection is
164 // necessary, so no need to call again.
167 void ConnectionFactoryImpl::ConnectImpl() {
168 if (socket_handle_.socket() && socket_handle_.socket()->IsConnected())
169 socket_handle_.socket()->Disconnect();
170 socket_handle_.Reset();
172 // TODO(zea): resolve proxies.
173 net::ProxyInfo proxy_info;
174 proxy_info.UseDirect();
175 net::SSLConfig ssl_config;
176 network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
178 int status = net::InitSocketHandleForTlsConnect(
179 net::HostPortPair::FromURL(mcs_endpoint_),
180 network_session_.get(),
184 net::kPrivacyModeDisabled,
185 net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_SOCKET),
187 base::Bind(&ConnectionFactoryImpl::OnConnectDone,
188 weak_ptr_factory_.GetWeakPtr()));
189 if (status != net::ERR_IO_PENDING)
190 OnConnectDone(status);
193 void ConnectionFactoryImpl::InitHandler() {
194 // May be null in tests.
195 mcs_proto::LoginRequest login_request;
196 if (!request_builder_.is_null()) {
197 request_builder_.Run(&login_request);
198 DCHECK(login_request.IsInitialized());
201 connection_handler_->Init(login_request, socket_handle_.socket());
204 scoped_ptr<net::BackoffEntry> ConnectionFactoryImpl::CreateBackoffEntry(
205 const net::BackoffEntry::Policy* const policy) {
206 return scoped_ptr<net::BackoffEntry>(new net::BackoffEntry(policy));
209 base::TimeTicks ConnectionFactoryImpl::NowTicks() {
210 return base::TimeTicks::Now();
213 void ConnectionFactoryImpl::OnConnectDone(int result) {
214 if (result != net::OK) {
215 LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result;
216 backoff_entry_->InformOfRequest(false);
217 UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionFailureErrorCode", result);
222 DVLOG(1) << "MCS endpoint socket connection success, starting handshake.";
226 void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) {
227 if (result == net::OK) {
228 // Handshake succeeded, reset the backoff.
230 backoff_reset_time_ = NowTicks();
231 previous_backoff_.swap(backoff_entry_);
232 backoff_entry_->Reset();
237 UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionDisconnectErrorCode", result);
239 // TODO(zea): Consider how to handle errors that may require some sort of
240 // user intervention (login page, etc.).
241 LOG(ERROR) << "Connection reset with error " << result;
242 backoff_entry_->InformOfRequest(false);