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