1 // Copyright (c) 2011 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 "net/http/http_proxy_client_socket_pool.h"
6
7 #include <algorithm>
8
9 #include "base/time.h"
10 #include "base/values.h"
11 #include "googleurl/src/gurl.h"
12 #include "net/base/load_flags.h"
13 #include "net/base/net_errors.h"
14 #include "net/http/http_network_session.h"
15 #include "net/http/http_proxy_client_socket.h"
16 #include "net/socket/client_socket_factory.h"
17 #include "net/socket/client_socket_handle.h"
18 #include "net/socket/client_socket_pool_base.h"
19 #include "net/socket/ssl_client_socket.h"
20 #include "net/socket/ssl_client_socket_pool.h"
21 #include "net/socket/transport_client_socket_pool.h"
22 #include "net/spdy/spdy_proxy_client_socket.h"
23 #include "net/spdy/spdy_session.h"
24 #include "net/spdy/spdy_session_pool.h"
25 #include "net/spdy/spdy_settings_storage.h"
26 #include "net/spdy/spdy_stream.h"
27
28 namespace net {
29
HttpProxySocketParams(const scoped_refptr<TransportSocketParams> & transport_params,const scoped_refptr<SSLSocketParams> & ssl_params,const GURL & request_url,const std::string & user_agent,HostPortPair endpoint,HttpAuthCache * http_auth_cache,HttpAuthHandlerFactory * http_auth_handler_factory,SpdySessionPool * spdy_session_pool,bool tunnel)30 HttpProxySocketParams::HttpProxySocketParams(
31 const scoped_refptr<TransportSocketParams>& transport_params,
32 const scoped_refptr<SSLSocketParams>& ssl_params,
33 const GURL& request_url,
34 const std::string& user_agent,
35 HostPortPair endpoint,
36 HttpAuthCache* http_auth_cache,
37 HttpAuthHandlerFactory* http_auth_handler_factory,
38 SpdySessionPool* spdy_session_pool,
39 bool tunnel)
40 : transport_params_(transport_params),
41 ssl_params_(ssl_params),
42 spdy_session_pool_(spdy_session_pool),
43 request_url_(request_url),
44 user_agent_(user_agent),
45 endpoint_(endpoint),
46 http_auth_cache_(tunnel ? http_auth_cache : NULL),
47 http_auth_handler_factory_(tunnel ? http_auth_handler_factory : NULL),
48 tunnel_(tunnel) {
49 DCHECK((transport_params == NULL && ssl_params != NULL) ||
50 (transport_params != NULL && ssl_params == NULL));
51 if (transport_params_)
52 ignore_limits_ = transport_params->ignore_limits();
53 else
54 ignore_limits_ = ssl_params->ignore_limits();
55 }
56
destination() const57 const HostResolver::RequestInfo& HttpProxySocketParams::destination() const {
58 if (transport_params_ == NULL)
59 return ssl_params_->transport_params()->destination();
60 else
61 return transport_params_->destination();
62 }
63
~HttpProxySocketParams()64 HttpProxySocketParams::~HttpProxySocketParams() {}
65
66 #ifdef ANDROID
getUID(uid_t * uid) const67 bool HttpProxySocketParams::getUID(uid_t *uid) const {
68 if (transport_params_ == NULL)
69 return ssl_params_->transport_params()->getUID(uid);
70 else
71 return transport_params_->getUID(uid);
72 }
73
setUID(uid_t uid)74 void HttpProxySocketParams::setUID(uid_t uid) {
75 if (transport_params_ == NULL)
76 ssl_params_->transport_params()->setUID(uid);
77 else
78 transport_params_->setUID(uid);
79 }
80 #endif
81
82 // HttpProxyConnectJobs will time out after this many seconds. Note this is on
83 // top of the timeout for the transport socket.
84 static const int kHttpProxyConnectJobTimeoutInSeconds = 30;
85
HttpProxyConnectJob(const std::string & group_name,const scoped_refptr<HttpProxySocketParams> & params,const base::TimeDelta & timeout_duration,TransportClientSocketPool * transport_pool,SSLClientSocketPool * ssl_pool,HostResolver * host_resolver,Delegate * delegate,NetLog * net_log)86 HttpProxyConnectJob::HttpProxyConnectJob(
87 const std::string& group_name,
88 const scoped_refptr<HttpProxySocketParams>& params,
89 const base::TimeDelta& timeout_duration,
90 TransportClientSocketPool* transport_pool,
91 SSLClientSocketPool* ssl_pool,
92 HostResolver* host_resolver,
93 Delegate* delegate,
94 NetLog* net_log)
95 : ConnectJob(group_name, timeout_duration, delegate,
96 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
97 params_(params),
98 transport_pool_(transport_pool),
99 ssl_pool_(ssl_pool),
100 resolver_(host_resolver),
101 ALLOW_THIS_IN_INITIALIZER_LIST(
102 callback_(this, &HttpProxyConnectJob::OnIOComplete)),
103 using_spdy_(false) {
104 }
105
~HttpProxyConnectJob()106 HttpProxyConnectJob::~HttpProxyConnectJob() {}
107
GetLoadState() const108 LoadState HttpProxyConnectJob::GetLoadState() const {
109 switch (next_state_) {
110 case STATE_TCP_CONNECT:
111 case STATE_TCP_CONNECT_COMPLETE:
112 case STATE_SSL_CONNECT:
113 case STATE_SSL_CONNECT_COMPLETE:
114 return transport_socket_handle_->GetLoadState();
115 case STATE_HTTP_PROXY_CONNECT:
116 case STATE_HTTP_PROXY_CONNECT_COMPLETE:
117 case STATE_SPDY_PROXY_CREATE_STREAM:
118 case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
119 return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
120 default:
121 NOTREACHED();
122 return LOAD_STATE_IDLE;
123 }
124 }
125
GetAdditionalErrorState(ClientSocketHandle * handle)126 void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) {
127 if (error_response_info_.cert_request_info) {
128 handle->set_ssl_error_response_info(error_response_info_);
129 handle->set_is_ssl_error(true);
130 }
131 }
132
OnIOComplete(int result)133 void HttpProxyConnectJob::OnIOComplete(int result) {
134 int rv = DoLoop(result);
135 if (rv != ERR_IO_PENDING)
136 NotifyDelegateOfCompletion(rv); // Deletes |this|
137 }
138
DoLoop(int result)139 int HttpProxyConnectJob::DoLoop(int result) {
140 DCHECK_NE(next_state_, STATE_NONE);
141
142 int rv = result;
143 do {
144 State state = next_state_;
145 next_state_ = STATE_NONE;
146 switch (state) {
147 case STATE_TCP_CONNECT:
148 DCHECK_EQ(OK, rv);
149 rv = DoTransportConnect();
150 break;
151 case STATE_TCP_CONNECT_COMPLETE:
152 rv = DoTransportConnectComplete(rv);
153 break;
154 case STATE_SSL_CONNECT:
155 DCHECK_EQ(OK, rv);
156 rv = DoSSLConnect();
157 break;
158 case STATE_SSL_CONNECT_COMPLETE:
159 rv = DoSSLConnectComplete(rv);
160 break;
161 case STATE_HTTP_PROXY_CONNECT:
162 DCHECK_EQ(OK, rv);
163 rv = DoHttpProxyConnect();
164 break;
165 case STATE_HTTP_PROXY_CONNECT_COMPLETE:
166 rv = DoHttpProxyConnectComplete(rv);
167 break;
168 case STATE_SPDY_PROXY_CREATE_STREAM:
169 DCHECK_EQ(OK, rv);
170 rv = DoSpdyProxyCreateStream();
171 break;
172 case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
173 rv = DoSpdyProxyCreateStreamComplete(rv);
174 break;
175 default:
176 NOTREACHED() << "bad state";
177 rv = ERR_FAILED;
178 break;
179 }
180 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
181
182 return rv;
183 }
184
DoTransportConnect()185 int HttpProxyConnectJob::DoTransportConnect() {
186 next_state_ = STATE_TCP_CONNECT_COMPLETE;
187 transport_socket_handle_.reset(new ClientSocketHandle());
188 return transport_socket_handle_->Init(
189 group_name(),
190 params_->transport_params(),
191 params_->transport_params()->destination().priority(),
192 &callback_,
193 transport_pool_,
194 net_log());
195 }
196
DoTransportConnectComplete(int result)197 int HttpProxyConnectJob::DoTransportConnectComplete(int result) {
198 if (result != OK)
199 return ERR_PROXY_CONNECTION_FAILED;
200
201 // Reset the timer to just the length of time allowed for HttpProxy handshake
202 // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
203 // longer to timeout than it should.
204 ResetTimer(base::TimeDelta::FromSeconds(
205 kHttpProxyConnectJobTimeoutInSeconds));
206
207 next_state_ = STATE_HTTP_PROXY_CONNECT;
208 return result;
209 }
210
DoSSLConnect()211 int HttpProxyConnectJob::DoSSLConnect() {
212 if (params_->tunnel()) {
213 HostPortProxyPair pair(params_->destination().host_port_pair(),
214 ProxyServer::Direct());
215 if (params_->spdy_session_pool()->HasSession(pair)) {
216 using_spdy_ = true;
217 next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
218 return OK;
219 }
220 }
221 next_state_ = STATE_SSL_CONNECT_COMPLETE;
222 transport_socket_handle_.reset(new ClientSocketHandle());
223 return transport_socket_handle_->Init(
224 group_name(), params_->ssl_params(),
225 params_->ssl_params()->transport_params()->destination().priority(),
226 &callback_, ssl_pool_, net_log());
227 }
228
DoSSLConnectComplete(int result)229 int HttpProxyConnectJob::DoSSLConnectComplete(int result) {
230 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
231 error_response_info_ = transport_socket_handle_->ssl_error_response_info();
232 DCHECK(error_response_info_.cert_request_info.get());
233 return result;
234 }
235 if (IsCertificateError(result)) {
236 if (params_->ssl_params()->load_flags() & LOAD_IGNORE_ALL_CERT_ERRORS)
237 result = OK;
238 else
239 // TODO(rch): allow the user to deal with proxy cert errors in the
240 // same way as server cert errors.
241 return ERR_PROXY_CERTIFICATE_INVALID;
242 }
243 if (result < 0) {
244 if (transport_socket_handle_->socket())
245 transport_socket_handle_->socket()->Disconnect();
246 return ERR_PROXY_CONNECTION_FAILED;
247 }
248
249 SSLClientSocket* ssl =
250 static_cast<SSLClientSocket*>(transport_socket_handle_->socket());
251 using_spdy_ = ssl->was_spdy_negotiated();
252
253 // Reset the timer to just the length of time allowed for HttpProxy handshake
254 // so that a fast SSL connection plus a slow HttpProxy failure doesn't take
255 // longer to timeout than it should.
256 ResetTimer(base::TimeDelta::FromSeconds(
257 kHttpProxyConnectJobTimeoutInSeconds));
258 // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy
259 // (one that we speak SPDY over SSL to, but to which we send HTTPS
260 // request directly instead of through CONNECT tunnels, then we
261 // need to add a predicate to this if statement so we fall through
262 // to the else case. (HttpProxyClientSocket currently acts as
263 // a "trusted" SPDY proxy).
264 if (using_spdy_ && params_->tunnel())
265 next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
266 else
267 next_state_ = STATE_HTTP_PROXY_CONNECT;
268 return result;
269 }
270
271 #ifdef ANDROID
272 // TODO(kristianm): Find out if Connect should block
273 #endif
DoHttpProxyConnect()274 int HttpProxyConnectJob::DoHttpProxyConnect() {
275 next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
276 const HostResolver::RequestInfo& tcp_destination = params_->destination();
277 const HostPortPair& proxy_server = tcp_destination.host_port_pair();
278
279 // Add a HttpProxy connection on top of the tcp socket.
280 transport_socket_.reset(
281 new HttpProxyClientSocket(transport_socket_handle_.release(),
282 params_->request_url(),
283 params_->user_agent(),
284 params_->endpoint(),
285 proxy_server,
286 params_->http_auth_cache(),
287 params_->http_auth_handler_factory(),
288 params_->tunnel(),
289 using_spdy_,
290 params_->ssl_params() != NULL));
291
292 #ifdef ANDROID
293 uid_t calling_uid = 0;
294 bool valid_uid = params_->transport_params()->getUID(&calling_uid);
295 #endif
296
297 return transport_socket_->Connect(&callback_
298 #ifdef ANDROID
299 , false
300 , valid_uid
301 , calling_uid
302 #endif
303 );
304 }
305
DoHttpProxyConnectComplete(int result)306 int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
307 if (result == OK || result == ERR_PROXY_AUTH_REQUESTED ||
308 result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
309 set_socket(transport_socket_.release());
310 }
311
312 return result;
313 }
314
DoSpdyProxyCreateStream()315 int HttpProxyConnectJob::DoSpdyProxyCreateStream() {
316 DCHECK(using_spdy_);
317 DCHECK(params_->tunnel());
318
319 HostPortProxyPair pair(params_->destination().host_port_pair(),
320 ProxyServer::Direct());
321 SpdySessionPool* spdy_pool = params_->spdy_session_pool();
322 scoped_refptr<SpdySession> spdy_session;
323 // It's possible that a session to the proxy has recently been created
324 if (spdy_pool->HasSession(pair)) {
325 if (transport_socket_handle_.get()) {
326 if (transport_socket_handle_->socket())
327 transport_socket_handle_->socket()->Disconnect();
328 transport_socket_handle_->Reset();
329 }
330 spdy_session = spdy_pool->Get(pair, net_log());
331 } else {
332 // Create a session direct to the proxy itself
333 int rv = spdy_pool->GetSpdySessionFromSocket(
334 pair, transport_socket_handle_.release(),
335 net_log(), OK, &spdy_session, /*using_ssl_*/ true);
336 if (rv < 0)
337 return rv;
338 }
339
340 next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
341 return spdy_session->CreateStream(params_->request_url(),
342 params_->destination().priority(),
343 &spdy_stream_, spdy_session->net_log(),
344 &callback_);
345 }
346
347 #ifdef ANDROID
348 // TODO(kristianm): Find out if Connect should block
349 #endif
DoSpdyProxyCreateStreamComplete(int result)350 int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) {
351 if (result < 0)
352 return result;
353
354 #ifdef ANDROID
355 uid_t calling_uid = 0;
356 bool valid_uid = params_->transport_params()->getUID(&calling_uid);
357 #endif
358
359 next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
360 transport_socket_.reset(
361 new SpdyProxyClientSocket(spdy_stream_,
362 params_->user_agent(),
363 params_->endpoint(),
364 params_->request_url(),
365 params_->destination().host_port_pair(),
366 params_->http_auth_cache(),
367 params_->http_auth_handler_factory()));
368 return transport_socket_->Connect(&callback_
369 #ifdef ANDROID
370 , false
371 , valid_uid
372 , calling_uid
373 #endif
374 );
375 }
376
ConnectInternal()377 int HttpProxyConnectJob::ConnectInternal() {
378 if (params_->transport_params())
379 next_state_ = STATE_TCP_CONNECT;
380 else
381 next_state_ = STATE_SSL_CONNECT;
382 return DoLoop(OK);
383 }
384
385 HttpProxyClientSocketPool::
HttpProxyConnectJobFactory(TransportClientSocketPool * transport_pool,SSLClientSocketPool * ssl_pool,HostResolver * host_resolver,NetLog * net_log)386 HttpProxyConnectJobFactory::HttpProxyConnectJobFactory(
387 TransportClientSocketPool* transport_pool,
388 SSLClientSocketPool* ssl_pool,
389 HostResolver* host_resolver,
390 NetLog* net_log)
391 : transport_pool_(transport_pool),
392 ssl_pool_(ssl_pool),
393 host_resolver_(host_resolver),
394 net_log_(net_log) {
395 base::TimeDelta max_pool_timeout = base::TimeDelta();
396 if (transport_pool_)
397 max_pool_timeout = transport_pool_->ConnectionTimeout();
398 if (ssl_pool_)
399 max_pool_timeout = std::max(max_pool_timeout,
400 ssl_pool_->ConnectionTimeout());
401 timeout_ = max_pool_timeout +
402 base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds);
403 }
404
405
406 ConnectJob*
NewConnectJob(const std::string & group_name,const PoolBase::Request & request,ConnectJob::Delegate * delegate) const407 HttpProxyClientSocketPool::HttpProxyConnectJobFactory::NewConnectJob(
408 const std::string& group_name,
409 const PoolBase::Request& request,
410 ConnectJob::Delegate* delegate) const {
411 return new HttpProxyConnectJob(group_name,
412 request.params(),
413 ConnectionTimeout(),
414 transport_pool_,
415 ssl_pool_,
416 host_resolver_,
417 delegate,
418 net_log_);
419 }
420
HttpProxyClientSocketPool(int max_sockets,int max_sockets_per_group,ClientSocketPoolHistograms * histograms,HostResolver * host_resolver,TransportClientSocketPool * transport_pool,SSLClientSocketPool * ssl_pool,NetLog * net_log)421 HttpProxyClientSocketPool::HttpProxyClientSocketPool(
422 int max_sockets,
423 int max_sockets_per_group,
424 ClientSocketPoolHistograms* histograms,
425 HostResolver* host_resolver,
426 TransportClientSocketPool* transport_pool,
427 SSLClientSocketPool* ssl_pool,
428 NetLog* net_log)
429 : transport_pool_(transport_pool),
430 ssl_pool_(ssl_pool),
431 base_(max_sockets, max_sockets_per_group, histograms,
432 base::TimeDelta::FromSeconds(
433 ClientSocketPool::unused_idle_socket_timeout()),
434 base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
435 new HttpProxyConnectJobFactory(transport_pool,
436 ssl_pool,
437 host_resolver,
438 net_log)) {}
439
~HttpProxyClientSocketPool()440 HttpProxyClientSocketPool::~HttpProxyClientSocketPool() {}
441
RequestSocket(const std::string & group_name,const void * socket_params,RequestPriority priority,ClientSocketHandle * handle,CompletionCallback * callback,const BoundNetLog & net_log)442 int HttpProxyClientSocketPool::RequestSocket(const std::string& group_name,
443 const void* socket_params,
444 RequestPriority priority,
445 ClientSocketHandle* handle,
446 CompletionCallback* callback,
447 const BoundNetLog& net_log) {
448 const scoped_refptr<HttpProxySocketParams>* casted_socket_params =
449 static_cast<const scoped_refptr<HttpProxySocketParams>*>(socket_params);
450
451 return base_.RequestSocket(group_name, *casted_socket_params, priority,
452 handle, callback, net_log);
453 }
454
RequestSockets(const std::string & group_name,const void * params,int num_sockets,const BoundNetLog & net_log)455 void HttpProxyClientSocketPool::RequestSockets(
456 const std::string& group_name,
457 const void* params,
458 int num_sockets,
459 const BoundNetLog& net_log) {
460 const scoped_refptr<HttpProxySocketParams>* casted_params =
461 static_cast<const scoped_refptr<HttpProxySocketParams>*>(params);
462
463 base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
464 }
465
CancelRequest(const std::string & group_name,ClientSocketHandle * handle)466 void HttpProxyClientSocketPool::CancelRequest(
467 const std::string& group_name,
468 ClientSocketHandle* handle) {
469 base_.CancelRequest(group_name, handle);
470 }
471
ReleaseSocket(const std::string & group_name,ClientSocket * socket,int id)472 void HttpProxyClientSocketPool::ReleaseSocket(const std::string& group_name,
473 ClientSocket* socket, int id) {
474 base_.ReleaseSocket(group_name, socket, id);
475 }
476
Flush()477 void HttpProxyClientSocketPool::Flush() {
478 base_.Flush();
479 }
480
CloseIdleSockets()481 void HttpProxyClientSocketPool::CloseIdleSockets() {
482 base_.CloseIdleSockets();
483 }
484
IdleSocketCount() const485 int HttpProxyClientSocketPool::IdleSocketCount() const {
486 return base_.idle_socket_count();
487 }
488
IdleSocketCountInGroup(const std::string & group_name) const489 int HttpProxyClientSocketPool::IdleSocketCountInGroup(
490 const std::string& group_name) const {
491 return base_.IdleSocketCountInGroup(group_name);
492 }
493
GetLoadState(const std::string & group_name,const ClientSocketHandle * handle) const494 LoadState HttpProxyClientSocketPool::GetLoadState(
495 const std::string& group_name, const ClientSocketHandle* handle) const {
496 return base_.GetLoadState(group_name, handle);
497 }
498
GetInfoAsValue(const std::string & name,const std::string & type,bool include_nested_pools) const499 DictionaryValue* HttpProxyClientSocketPool::GetInfoAsValue(
500 const std::string& name,
501 const std::string& type,
502 bool include_nested_pools) const {
503 DictionaryValue* dict = base_.GetInfoAsValue(name, type);
504 if (include_nested_pools) {
505 ListValue* list = new ListValue();
506 if (transport_pool_) {
507 list->Append(transport_pool_->GetInfoAsValue("transport_socket_pool",
508 "transport_socket_pool",
509 true));
510 }
511 if (ssl_pool_) {
512 list->Append(ssl_pool_->GetInfoAsValue("ssl_socket_pool",
513 "ssl_socket_pool",
514 true));
515 }
516 dict->Set("nested_pools", list);
517 }
518 return dict;
519 }
520
ConnectionTimeout() const521 base::TimeDelta HttpProxyClientSocketPool::ConnectionTimeout() const {
522 return base_.ConnectionTimeout();
523 }
524
histograms() const525 ClientSocketPoolHistograms* HttpProxyClientSocketPool::histograms() const {
526 return base_.histograms();
527 }
528
529 } // namespace net
530