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