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