OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "jingle/notifier/base/proxy_resolving_client_socket.h" | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/bind.h" | |
9 #include "base/bind_helpers.h" | |
10 #include "base/compiler_specific.h" | |
11 #include "base/logging.h" | |
12 #include "googleurl/src/gurl.h" | |
13 #include "net/base/io_buffer.h" | |
14 #include "net/base/net_errors.h" | |
15 #include "net/http/http_network_session.h" | |
16 #include "net/socket/client_socket_handle.h" | |
17 #include "net/socket/client_socket_pool_manager.h" | |
18 #include "net/url_request/url_request_context.h" | |
19 #include "net/url_request/url_request_context_getter.h" | |
20 | |
21 namespace notifier { | |
22 | |
23 ProxyResolvingClientSocket::ProxyResolvingClientSocket( | |
24 net::ClientSocketFactory* socket_factory, | |
25 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, | |
26 const net::SSLConfig& ssl_config, | |
27 const net::HostPortPair& dest_host_port_pair) | |
28 : ALLOW_THIS_IN_INITIALIZER_LIST(proxy_resolve_callback_( | |
29 base::Bind(&ProxyResolvingClientSocket::ProcessProxyResolveDone, | |
30 base::Unretained(this)))), | |
31 ALLOW_THIS_IN_INITIALIZER_LIST(connect_callback_( | |
32 base::Bind(&ProxyResolvingClientSocket::ProcessConnectDone, | |
33 base::Unretained(this)))), | |
34 ssl_config_(ssl_config), | |
35 pac_request_(NULL), | |
36 dest_host_port_pair_(dest_host_port_pair), | |
37 tried_direct_connect_fallback_(false), | |
38 bound_net_log_( | |
39 net::BoundNetLog::Make( | |
40 request_context_getter->GetURLRequestContext()->net_log(), | |
41 net::NetLog::SOURCE_SOCKET)), | |
42 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
43 DCHECK(request_context_getter); | |
44 net::URLRequestContext* request_context = | |
45 request_context_getter->GetURLRequestContext(); | |
46 DCHECK(request_context); | |
47 DCHECK(!dest_host_port_pair_.host().empty()); | |
48 DCHECK_GT(dest_host_port_pair_.port(), 0); | |
49 net::HttpNetworkSession::Params session_params; | |
50 session_params.client_socket_factory = socket_factory; | |
51 session_params.host_resolver = request_context->host_resolver(); | |
52 session_params.cert_verifier = request_context->cert_verifier(); | |
53 // TODO(rkn): This is NULL because ServerBoundCertService is not thread safe. | |
54 session_params.server_bound_cert_service = NULL; | |
55 // transport_security_state is NULL because it's not thread safe. | |
56 session_params.transport_security_state = NULL; | |
57 session_params.proxy_service = request_context->proxy_service(); | |
58 session_params.ssl_config_service = request_context->ssl_config_service(); | |
59 session_params.http_auth_handler_factory = | |
60 request_context->http_auth_handler_factory(); | |
61 session_params.network_delegate = request_context->network_delegate(); | |
62 session_params.http_server_properties = | |
63 request_context->http_server_properties(); | |
64 session_params.net_log = request_context->net_log(); | |
65 network_session_ = new net::HttpNetworkSession(session_params); | |
66 } | |
67 | |
68 ProxyResolvingClientSocket::~ProxyResolvingClientSocket() { | |
69 Disconnect(); | |
70 } | |
71 | |
72 int ProxyResolvingClientSocket::Read(net::IOBuffer* buf, int buf_len, | |
73 const net::CompletionCallback& callback) { | |
74 if (transport_.get() && transport_->socket()) | |
75 return transport_->socket()->Read(buf, buf_len, callback); | |
76 NOTREACHED(); | |
77 return net::ERR_SOCKET_NOT_CONNECTED; | |
78 } | |
79 | |
80 int ProxyResolvingClientSocket::Write( | |
81 net::IOBuffer* buf, | |
82 int buf_len, | |
83 const net::CompletionCallback& callback) { | |
84 if (transport_.get() && transport_->socket()) | |
85 return transport_->socket()->Write(buf, buf_len, callback); | |
86 NOTREACHED(); | |
87 return net::ERR_SOCKET_NOT_CONNECTED; | |
88 } | |
89 | |
90 bool ProxyResolvingClientSocket::SetReceiveBufferSize(int32 size) { | |
91 if (transport_.get() && transport_->socket()) | |
92 return transport_->socket()->SetReceiveBufferSize(size); | |
93 NOTREACHED(); | |
94 return false; | |
95 } | |
96 | |
97 bool ProxyResolvingClientSocket::SetSendBufferSize(int32 size) { | |
98 if (transport_.get() && transport_->socket()) | |
99 return transport_->socket()->SetSendBufferSize(size); | |
100 NOTREACHED(); | |
101 return false; | |
102 } | |
103 | |
104 int ProxyResolvingClientSocket::Connect( | |
105 const net::CompletionCallback& callback) { | |
106 DCHECK(user_connect_callback_.is_null()); | |
107 | |
108 tried_direct_connect_fallback_ = false; | |
109 | |
110 // First we try and resolve the proxy. | |
111 GURL url("http://" + dest_host_port_pair_.ToString()); | |
112 DCHECK(url.is_valid()); | |
113 int status = network_session_->proxy_service()->ResolveProxy( | |
114 url, | |
115 &proxy_info_, | |
116 proxy_resolve_callback_, | |
117 &pac_request_, | |
118 bound_net_log_); | |
119 if (status != net::ERR_IO_PENDING) { | |
120 // We defer execution of ProcessProxyResolveDone instead of calling it | |
121 // directly here for simplicity. From the caller's point of view, | |
122 // the connect always happens asynchronously. | |
123 MessageLoop* message_loop = MessageLoop::current(); | |
124 CHECK(message_loop); | |
125 message_loop->PostTask( | |
126 FROM_HERE, | |
127 base::Bind(&ProxyResolvingClientSocket::ProcessProxyResolveDone, | |
128 weak_factory_.GetWeakPtr(), status)); | |
129 } | |
130 user_connect_callback_ = callback; | |
131 return net::ERR_IO_PENDING; | |
132 } | |
133 | |
134 void ProxyResolvingClientSocket::RunUserConnectCallback(int status) { | |
135 DCHECK_LE(status, net::OK); | |
136 net::CompletionCallback user_connect_callback = user_connect_callback_; | |
137 user_connect_callback_.Reset(); | |
138 user_connect_callback.Run(status); | |
139 } | |
140 | |
141 // Always runs asynchronously. | |
142 void ProxyResolvingClientSocket::ProcessProxyResolveDone(int status) { | |
143 pac_request_ = NULL; | |
144 | |
145 DCHECK_NE(status, net::ERR_IO_PENDING); | |
146 if (status == net::OK) { | |
147 // Remove unsupported proxies from the list. | |
148 proxy_info_.RemoveProxiesWithoutScheme( | |
149 net::ProxyServer::SCHEME_DIRECT | | |
150 net::ProxyServer::SCHEME_HTTP | net::ProxyServer::SCHEME_HTTPS | | |
151 net::ProxyServer::SCHEME_SOCKS4 | net::ProxyServer::SCHEME_SOCKS5); | |
152 | |
153 if (proxy_info_.is_empty()) { | |
154 // No proxies/direct to choose from. This happens when we don't support | |
155 // any of the proxies in the returned list. | |
156 status = net::ERR_NO_SUPPORTED_PROXIES; | |
157 } | |
158 } | |
159 | |
160 // Since we are faking the URL, it is possible that no proxies match our URL. | |
161 // Try falling back to a direct connection if we have not tried that before. | |
162 if (status != net::OK) { | |
163 if (!tried_direct_connect_fallback_) { | |
164 tried_direct_connect_fallback_ = true; | |
165 proxy_info_.UseDirect(); | |
166 } else { | |
167 CloseTransportSocket(); | |
168 RunUserConnectCallback(status); | |
169 return; | |
170 } | |
171 } | |
172 | |
173 transport_.reset(new net::ClientSocketHandle); | |
174 // Now that we have resolved the proxy, we need to connect. | |
175 status = net::InitSocketHandleForRawConnect( | |
176 dest_host_port_pair_, network_session_.get(), proxy_info_, ssl_config_, | |
177 ssl_config_, bound_net_log_, transport_.get(), connect_callback_); | |
178 if (status != net::ERR_IO_PENDING) { | |
179 // Since this method is always called asynchronously. it is OK to call | |
180 // ProcessConnectDone synchronously. | |
181 ProcessConnectDone(status); | |
182 } | |
183 } | |
184 | |
185 void ProxyResolvingClientSocket::ProcessConnectDone(int status) { | |
186 if (status != net::OK) { | |
187 // If the connection fails, try another proxy. | |
188 status = ReconsiderProxyAfterError(status); | |
189 // ReconsiderProxyAfterError either returns an error (in which case it is | |
190 // not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering | |
191 // another proxy. | |
192 DCHECK_NE(status, net::OK); | |
193 if (status == net::ERR_IO_PENDING) | |
194 // Proxy reconsideration pending. Return. | |
195 return; | |
196 CloseTransportSocket(); | |
197 } else { | |
198 ReportSuccessfulProxyConnection(); | |
199 } | |
200 RunUserConnectCallback(status); | |
201 } | |
202 | |
203 // TODO(sanjeevr): This has largely been copied from | |
204 // HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError. This should be | |
205 // refactored into some common place. | |
206 // This method reconsiders the proxy on certain errors. If it does reconsider | |
207 // a proxy it always returns ERR_IO_PENDING and posts a call to | |
208 // ProcessProxyResolveDone with the result of the reconsideration. | |
209 int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) { | |
210 DCHECK(!pac_request_); | |
211 DCHECK_NE(error, net::OK); | |
212 DCHECK_NE(error, net::ERR_IO_PENDING); | |
213 // A failure to resolve the hostname or any error related to establishing a | |
214 // TCP connection could be grounds for trying a new proxy configuration. | |
215 // | |
216 // Why do this when a hostname cannot be resolved? Some URLs only make sense | |
217 // to proxy servers. The hostname in those URLs might fail to resolve if we | |
218 // are still using a non-proxy config. We need to check if a proxy config | |
219 // now exists that corresponds to a proxy server that could load the URL. | |
220 // | |
221 switch (error) { | |
222 case net::ERR_PROXY_CONNECTION_FAILED: | |
223 case net::ERR_NAME_NOT_RESOLVED: | |
224 case net::ERR_INTERNET_DISCONNECTED: | |
225 case net::ERR_ADDRESS_UNREACHABLE: | |
226 case net::ERR_CONNECTION_CLOSED: | |
227 case net::ERR_CONNECTION_RESET: | |
228 case net::ERR_CONNECTION_REFUSED: | |
229 case net::ERR_CONNECTION_ABORTED: | |
230 case net::ERR_TIMED_OUT: | |
231 case net::ERR_TUNNEL_CONNECTION_FAILED: | |
232 case net::ERR_SOCKS_CONNECTION_FAILED: | |
233 break; | |
234 case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE: | |
235 // Remap the SOCKS-specific "host unreachable" error to a more | |
236 // generic error code (this way consumers like the link doctor | |
237 // know to substitute their error page). | |
238 // | |
239 // Note that if the host resolving was done by the SOCSK5 proxy, we can't | |
240 // differentiate between a proxy-side "host not found" versus a proxy-side | |
241 // "address unreachable" error, and will report both of these failures as | |
242 // ERR_ADDRESS_UNREACHABLE. | |
243 return net::ERR_ADDRESS_UNREACHABLE; | |
244 default: | |
245 return error; | |
246 } | |
247 | |
248 if (proxy_info_.is_https() && ssl_config_.send_client_cert) { | |
249 network_session_->ssl_client_auth_cache()->Remove( | |
250 proxy_info_.proxy_server().host_port_pair().ToString()); | |
251 } | |
252 | |
253 GURL url("http://" + dest_host_port_pair_.ToString()); | |
254 int rv = network_session_->proxy_service()->ReconsiderProxyAfterError( | |
255 url, &proxy_info_, proxy_resolve_callback_, &pac_request_, | |
256 bound_net_log_); | |
257 if (rv == net::OK || rv == net::ERR_IO_PENDING) { | |
258 CloseTransportSocket(); | |
259 } else { | |
260 // If ReconsiderProxyAfterError() failed synchronously, it means | |
261 // there was nothing left to fall-back to, so fail the transaction | |
262 // with the last connection error we got. | |
263 rv = error; | |
264 } | |
265 | |
266 // We either have new proxy info or there was an error in falling back. | |
267 // In both cases we want to post ProcessProxyResolveDone (in the error case | |
268 // we might still want to fall back a direct connection). | |
269 if (rv != net::ERR_IO_PENDING) { | |
270 MessageLoop* message_loop = MessageLoop::current(); | |
271 CHECK(message_loop); | |
272 message_loop->PostTask( | |
273 FROM_HERE, | |
274 base::Bind(&ProxyResolvingClientSocket::ProcessProxyResolveDone, | |
275 weak_factory_.GetWeakPtr(), rv)); | |
276 // Since we potentially have another try to go (trying the direct connect) | |
277 // set the return code code to ERR_IO_PENDING. | |
278 rv = net::ERR_IO_PENDING; | |
279 } | |
280 return rv; | |
281 } | |
282 | |
283 void ProxyResolvingClientSocket::ReportSuccessfulProxyConnection() { | |
284 network_session_->proxy_service()->ReportSuccess(proxy_info_); | |
285 } | |
286 | |
287 void ProxyResolvingClientSocket::Disconnect() { | |
288 CloseTransportSocket(); | |
289 if (pac_request_) | |
290 network_session_->proxy_service()->CancelPacRequest(pac_request_); | |
291 user_connect_callback_.Reset(); | |
292 } | |
293 | |
294 bool ProxyResolvingClientSocket::IsConnected() const { | |
295 if (!transport_.get() || !transport_->socket()) | |
296 return false; | |
297 return transport_->socket()->IsConnected(); | |
298 } | |
299 | |
300 bool ProxyResolvingClientSocket::IsConnectedAndIdle() const { | |
301 if (!transport_.get() || !transport_->socket()) | |
302 return false; | |
303 return transport_->socket()->IsConnectedAndIdle(); | |
304 } | |
305 | |
306 int ProxyResolvingClientSocket::GetPeerAddress( | |
307 net::IPEndPoint* address) const { | |
308 if (transport_.get() && transport_->socket()) | |
309 return transport_->socket()->GetPeerAddress(address); | |
310 NOTREACHED(); | |
311 return net::ERR_SOCKET_NOT_CONNECTED; | |
312 } | |
313 | |
314 int ProxyResolvingClientSocket::GetLocalAddress( | |
315 net::IPEndPoint* address) const { | |
316 if (transport_.get() && transport_->socket()) | |
317 return transport_->socket()->GetLocalAddress(address); | |
318 NOTREACHED(); | |
319 return net::ERR_SOCKET_NOT_CONNECTED; | |
320 } | |
321 | |
322 const net::BoundNetLog& ProxyResolvingClientSocket::NetLog() const { | |
323 if (transport_.get() && transport_->socket()) | |
324 return transport_->socket()->NetLog(); | |
325 NOTREACHED(); | |
326 return bound_net_log_; | |
327 } | |
328 | |
329 void ProxyResolvingClientSocket::SetSubresourceSpeculation() { | |
330 if (transport_.get() && transport_->socket()) | |
331 transport_->socket()->SetSubresourceSpeculation(); | |
332 else | |
333 NOTREACHED(); | |
334 } | |
335 | |
336 void ProxyResolvingClientSocket::SetOmniboxSpeculation() { | |
337 if (transport_.get() && transport_->socket()) | |
338 transport_->socket()->SetOmniboxSpeculation(); | |
339 else | |
340 NOTREACHED(); | |
341 } | |
342 | |
343 bool ProxyResolvingClientSocket::WasEverUsed() const { | |
344 if (transport_.get() && transport_->socket()) | |
345 return transport_->socket()->WasEverUsed(); | |
346 NOTREACHED(); | |
347 return false; | |
348 } | |
349 | |
350 bool ProxyResolvingClientSocket::UsingTCPFastOpen() const { | |
351 if (transport_.get() && transport_->socket()) | |
352 return transport_->socket()->UsingTCPFastOpen(); | |
353 NOTREACHED(); | |
354 return false; | |
355 } | |
356 | |
357 int64 ProxyResolvingClientSocket::NumBytesRead() const { | |
358 if (transport_.get() && transport_->socket()) | |
359 return transport_->socket()->NumBytesRead(); | |
360 NOTREACHED(); | |
361 return -1; | |
362 } | |
363 | |
364 base::TimeDelta ProxyResolvingClientSocket::GetConnectTimeMicros() const { | |
365 if (transport_.get() && transport_->socket()) | |
366 return transport_->socket()->GetConnectTimeMicros(); | |
367 NOTREACHED(); | |
368 return base::TimeDelta::FromMicroseconds(-1); | |
369 } | |
370 | |
371 bool ProxyResolvingClientSocket::WasNpnNegotiated() const { | |
372 return false; | |
373 } | |
374 | |
375 net::NextProto ProxyResolvingClientSocket::GetNegotiatedProtocol() const { | |
376 if (transport_.get() && transport_->socket()) | |
377 return transport_->socket()->GetNegotiatedProtocol(); | |
378 NOTREACHED(); | |
379 return net::kProtoUnknown; | |
380 } | |
381 | |
382 bool ProxyResolvingClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) { | |
383 return false; | |
384 } | |
385 | |
386 void ProxyResolvingClientSocket::CloseTransportSocket() { | |
387 if (transport_.get() && transport_->socket()) | |
388 transport_->socket()->Disconnect(); | |
389 transport_.reset(); | |
390 } | |
391 | |
392 } // namespace notifier | |
OLD | NEW |