Update ASan/Android runtime and setup script to LLVM r200682.
[chromium-blink-merge.git] / google_apis / gcm / engine / connection_factory_impl.cc
blob7ae40be111b7785107aae5bb950d14da444652b9
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"
20 namespace gcm {
22 namespace {
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.
32 // Backoff policy.
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.
39 10000, // 10 seconds.
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.
46 0.5, // 50%.
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.
53 -1,
55 // Don't use initial delay unless the last request was an error.
56 false,
59 } // namespace
61 ConnectionFactoryImpl::ConnectionFactoryImpl(
62 const GURL& mcs_endpoint,
63 scoped_refptr<net::HttpNetworkSession> network_session,
64 net::NetLog* net_log)
65 : mcs_endpoint_(mcs_endpoint),
66 network_session_(network_session),
67 net_log_(net_log),
68 connecting_(false),
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),
90 read_callback,
91 write_callback,
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_);
103 connecting_ = true;
104 if (backoff_entry_->ShouldRejectRequest()) {
105 DVLOG(1) << "Delaying MCS endpoint connection for "
106 << backoff_entry_->GetTimeUntilRelease().InMilliseconds()
107 << " milliseconds.";
108 base::MessageLoop::current()->PostDelayedTask(
109 FROM_HERE,
110 base::Bind(&ConnectionFactoryImpl::Connect,
111 weak_ptr_factory_.GetWeakPtr()),
112 backoff_entry_->GetTimeUntilRelease());
113 return;
116 DVLOG(1) << "Attempting connection to MCS endpoint.";
117 ConnectImpl();
120 bool ConnectionFactoryImpl::IsEndpointReachable() const {
121 return connection_handler_ &&
122 connection_handler_->CanSendMessage() &&
123 !connecting_;
126 void ConnectionFactoryImpl::SignalConnectionReset() {
127 if (connecting_)
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();
138 Connect();
141 base::TimeTicks ConnectionFactoryImpl::NextRetryAttempt() const {
142 if (!backoff_entry_)
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)
150 return;
152 // TODO(zea): implement different backoff/retry policies based on connection
153 // type.
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(),
181 proxy_info,
182 ssl_config,
183 ssl_config,
184 net::kPrivacyModeDisabled,
185 net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_SOCKET),
186 &socket_handle_,
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);
218 Connect();
219 return;
222 DVLOG(1) << "MCS endpoint socket connection success, starting handshake.";
223 InitHandler();
226 void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) {
227 if (result == net::OK) {
228 // Handshake succeeded, reset the backoff.
229 connecting_ = false;
230 backoff_reset_time_ = NowTicks();
231 previous_backoff_.swap(backoff_entry_);
232 backoff_entry_->Reset();
233 return;
236 if (!connecting_)
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);
243 Connect();
246 } // namespace gcm