• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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