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