Home | History | Annotate | Download | only in http
      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 
     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 
     57 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 
     64 HttpProxySocketParams::~HttpProxySocketParams() {}
     65 
     66 #ifdef ANDROID
     67 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 
     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 
     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 
    106 HttpProxyConnectJob::~HttpProxyConnectJob() {}
    107 
    108 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 
    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 
    133 void HttpProxyConnectJob::OnIOComplete(int result) {
    134   int rv = DoLoop(result);
    135   if (rv != ERR_IO_PENDING)
    136     NotifyDelegateOfCompletion(rv);  // Deletes |this|
    137 }
    138 
    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 
    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 
    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 
    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 
    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
    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 
    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 
    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
    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 
    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::
    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*
    407 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 
    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 
    440 HttpProxyClientSocketPool::~HttpProxyClientSocketPool() {}
    441 
    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 
    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 
    466 void HttpProxyClientSocketPool::CancelRequest(
    467     const std::string& group_name,
    468     ClientSocketHandle* handle) {
    469   base_.CancelRequest(group_name, handle);
    470 }
    471 
    472 void HttpProxyClientSocketPool::ReleaseSocket(const std::string& group_name,
    473                                               ClientSocket* socket, int id) {
    474   base_.ReleaseSocket(group_name, socket, id);
    475 }
    476 
    477 void HttpProxyClientSocketPool::Flush() {
    478   base_.Flush();
    479 }
    480 
    481 void HttpProxyClientSocketPool::CloseIdleSockets() {
    482   base_.CloseIdleSockets();
    483 }
    484 
    485 int HttpProxyClientSocketPool::IdleSocketCount() const {
    486   return base_.idle_socket_count();
    487 }
    488 
    489 int HttpProxyClientSocketPool::IdleSocketCountInGroup(
    490     const std::string& group_name) const {
    491   return base_.IdleSocketCountInGroup(group_name);
    492 }
    493 
    494 LoadState HttpProxyClientSocketPool::GetLoadState(
    495     const std::string& group_name, const ClientSocketHandle* handle) const {
    496   return base_.GetLoadState(group_name, handle);
    497 }
    498 
    499 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 
    521 base::TimeDelta HttpProxyClientSocketPool::ConnectionTimeout() const {
    522   return base_.ConnectionTimeout();
    523 }
    524 
    525 ClientSocketPoolHistograms* HttpProxyClientSocketPool::histograms() const {
    526   return base_.histograms();
    527 }
    528 
    529 }  // namespace net
    530