OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "google_apis/gcm/engine/connection_factory_impl.h" | |
6 | |
7 #include "base/message_loop/message_loop.h" | |
8 #include "google_apis/gcm/engine/connection_handler_impl.h" | |
9 #include "google_apis/gcm/protocol/mcs.pb.h" | |
10 #include "net/base/net_errors.h" | |
11 #include "net/http/http_network_session.h" | |
12 #include "net/http/http_request_headers.h" | |
13 #include "net/proxy/proxy_info.h" | |
14 #include "net/socket/client_socket_handle.h" | |
15 #include "net/socket/client_socket_pool_manager.h" | |
16 #include "net/ssl/ssl_config_service.h" | |
17 | |
18 namespace gcm { | |
19 | |
20 namespace { | |
21 | |
22 // The amount of time a Socket read should wait before timing out. | |
23 const int kReadTimeoutMs = 30000; // 30 seconds. | |
24 | |
25 // Backoff policy. | |
26 const net::BackoffEntry::Policy kConnectionBackoffPolicy = { | |
27 // Number of initial errors (in sequence) to ignore before applying | |
28 // exponential back-off rules. | |
29 0, | |
30 | |
31 // Initial delay for exponential back-off in ms. | |
32 10000, // 10 seconds. | |
33 | |
34 // Factor by which the waiting time will be multiplied. | |
35 2, | |
36 | |
37 // Fuzzing percentage. ex: 10% will spread requests randomly | |
38 // between 90%-100% of the calculated time. | |
39 0.2, // 20%. | |
40 | |
41 // Maximum amount of time we are willing to delay our request in ms. | |
42 1000 * 3600 * 4, // 4 hours. | |
43 | |
44 // Time to keep an entry from being discarded even when it | |
45 // has no significant state, -1 to never discard. | |
46 -1, | |
47 | |
48 // Don't use initial delay unless the last request was an error. | |
49 false, | |
50 }; | |
51 | |
52 // Callback for hostname resolution attempts. Just returns an error if no | |
53 // addresses were resolved. | |
54 int OnResolutionDone(const net::AddressList& address_list, | |
55 const net::BoundNetLog& net_log) { | |
56 if (address_list.empty()) { | |
57 LOG(ERROR) << "Failed to resolve hostname."; | |
58 return net::ERR_NAME_RESOLUTION_FAILED; | |
59 } | |
60 DVLOG(1) << "MCS Hostname resolution success."; | |
61 return net::OK; | |
62 } | |
63 | |
64 } // namespace | |
65 | |
66 ConnectionFactoryImpl::ConnectionFactoryImpl( | |
67 const GURL& mcs_endpoint, | |
68 scoped_refptr<net::HttpNetworkSession> network_session, | |
69 net::NetLog* net_log) | |
70 : mcs_endpoint_(mcs_endpoint), | |
71 network_session_(network_session), | |
72 net_log_(net_log), | |
73 backoff_entry_(&kConnectionBackoffPolicy), | |
74 weak_ptr_factory_(this) { | |
75 } | |
76 | |
77 ConnectionFactoryImpl::~ConnectionFactoryImpl() { | |
78 } | |
79 | |
80 ConnectionHandler* ConnectionFactoryImpl::BuildConnectionHandler( | |
81 const ConnectionHandler::ProtoReceivedCallback& read_callback, | |
82 const ConnectionHandler::ProtoSentCallback& write_callback) { | |
83 DCHECK(!connection_handler_.get()); | |
akalin
2013/11/02 00:15:43
i feel like you should do:
if (!connection_handle
Nicolas Zea
2013/11/15 23:37:35
Assuming you mean if (connection_handler_), done.
| |
84 net::NetworkChangeNotifier::AddIPAddressObserver(this); | |
85 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
86 connection_handler_.reset( | |
87 new ConnectionHandlerImpl( | |
88 base::TimeDelta::FromMilliseconds(kReadTimeoutMs), | |
89 read_callback, | |
90 write_callback, | |
91 base::Bind(&ConnectionFactoryImpl::ConnectionHandlerCallback, | |
92 weak_ptr_factory_.GetWeakPtr()))); | |
93 return connection_handler_.get(); | |
94 } | |
95 | |
96 void ConnectionFactoryImpl::Connect( | |
97 const mcs_proto::LoginRequest& login_request) { | |
98 DCHECK(connection_handler_.get()); | |
akalin
2013/11/02 00:15:43
no .get()
Nicolas Zea
2013/11/15 23:37:35
Done.
| |
99 DCHECK(!IsEndpointReachable()); | |
100 | |
101 login_request_ = login_request; | |
102 | |
103 ConnectImpl(); | |
104 } | |
105 | |
106 bool ConnectionFactoryImpl::IsEndpointReachable() const { | |
akalin
2013/11/02 00:15:43
no .get() (and everywhere else in a boolean contex
Nicolas Zea
2013/11/15 23:37:35
Done.
| |
107 return connection_handler_.get() && connection_handler_->CanSendMessage(); | |
108 } | |
109 | |
110 void ConnectionFactoryImpl::OnConnectionTypeChanged( | |
111 net::NetworkChangeNotifier::ConnectionType type) { | |
112 // TODO(zea): implement different backoff/retry policies based on connection | |
113 // type. | |
114 DVLOG(1) << "Connection type changed to " << type << ", resetting backoff."; | |
115 backoff_entry_.Reset(); | |
116 // ConnectImpl() should be retrying with backoff already if a connection is | |
117 // necessary, so no need to call again. | |
118 } | |
119 | |
120 void ConnectionFactoryImpl::OnIPAddressChanged() { | |
121 DVLOG(1) << "IP Address changed, resetting backoff."; | |
122 backoff_entry_.Reset(); | |
123 // ConnectImpl() should be retrying with backoff already if a connection is | |
124 // necessary, so no need to call again. | |
125 } | |
126 | |
127 void ConnectionFactoryImpl::ConnectImpl() { | |
128 DCHECK(!IsEndpointReachable()); | |
129 | |
130 if (backoff_entry_.ShouldRejectRequest()) { | |
akalin
2013/11/02 00:15:43
i feel like this logic should be moved into Connec
Nicolas Zea
2013/11/15 23:37:35
Assuming you mean move to Connect, done.
| |
131 DVLOG(1) << "Delaying MCS endpoint connection for " | |
132 << backoff_entry_.GetTimeUntilRelease().InSeconds() << " seconds."; | |
133 base::MessageLoop::current()->PostDelayedTask( | |
134 FROM_HERE, | |
135 base::Bind(&ConnectionFactoryImpl::ConnectImpl, | |
136 weak_ptr_factory_.GetWeakPtr()), | |
137 backoff_entry_.GetTimeUntilRelease()); | |
138 return; | |
139 } | |
140 | |
141 DVLOG(1) << "Attempting connection to MCS endpoint."; | |
142 | |
143 // TODO(zea): resolve proxies. | |
144 net::ProxyInfo proxy_info; | |
145 proxy_info.UseDirect(); | |
146 net::SSLConfig ssl_config; | |
147 network_session_->ssl_config_service()->GetSSLConfig(&ssl_config); | |
148 | |
149 // TODO(zea): This should probably be a raw request, but there's no good | |
akalin
2013/11/02 00:15:43
yeah, I think this should be a raw request. having
Nicolas Zea
2013/11/15 23:37:35
Raw connect takes a host/port pair, and prepends a
| |
150 // way to force SSL for a raw request yet. Instead, pretend to be an https | |
151 // request (which provides hostname resolution as well). | |
152 int status = net::InitSocketHandleForHttpRequest( | |
153 mcs_endpoint_, | |
154 net::HttpRequestHeaders(), | |
155 0, // No load flags. | |
156 net::MEDIUM, // Normal priority. | |
157 network_session_.get(), | |
158 proxy_info, | |
159 false, // Don't force SPDY over SSL. | |
160 false, // Don't want SPDY over NPN. | |
161 ssl_config, | |
162 ssl_config, | |
163 net::kPrivacyModeDisabled, | |
164 net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_SOCKET), | |
165 &socket_handle_, | |
166 base::Bind(&OnResolutionDone), | |
167 base::Bind(&ConnectionFactoryImpl::OnConnectDone, | |
168 weak_ptr_factory_.GetWeakPtr())); | |
169 if (status != net::ERR_IO_PENDING) | |
170 OnConnectDone(status); | |
171 } | |
172 | |
173 void ConnectionFactoryImpl::OnConnectDone(int result) { | |
174 if (result != net::OK) { | |
175 LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result; | |
176 backoff_entry_.InformOfRequest(false); | |
177 ConnectImpl(); | |
178 return; | |
179 } | |
180 | |
181 DVLOG(1) << "MCS endpoint connection success."; | |
182 | |
183 // Reset the backoff. | |
184 backoff_entry_.Reset(); | |
185 | |
186 // Login to the server. | |
187 connection_handler_->Init(login_request_, socket_handle_.PassSocket()); | |
188 } | |
189 | |
190 void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) { | |
191 // TODO(zea): Consider how to handle errors that may require some sort of | |
192 // user intervention (login page, etc.). | |
193 LOG(ERROR) << "Connection reset with error " << result; | |
194 backoff_entry_.InformOfRequest(false); | |
195 ConnectImpl(); | |
196 } | |
197 | |
198 } // namespace gcm | |
OLD | NEW |