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