tcp_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/socket/tcp_client_socket_pool.h"
6
7#include "base/compiler_specific.h"
8#include "base/logging.h"
9#include "base/message_loop.h"
10#include "base/metrics/histogram.h"
11#include "base/string_util.h"
12#include "base/time.h"
13#include "net/base/net_log.h"
14#include "net/base/net_errors.h"
15#include "net/socket/client_socket_factory.h"
16#include "net/socket/client_socket_handle.h"
17#include "net/socket/client_socket_pool_base.h"
18#include "net/socket/tcp_client_socket.h"
19
20using base::TimeDelta;
21
22namespace net {
23
24TCPSocketParams::TCPSocketParams(const HostPortPair& host_port_pair,
25                                 RequestPriority priority, const GURL& referrer,
26                                 bool disable_resolver_cache, bool ignore_limits)
27    : destination_(host_port_pair)
28#ifdef ANDROID
29      , ignore_limits_(ignore_limits)
30#endif
31    {
32  Initialize(priority, referrer, disable_resolver_cache);
33}
34
35// TODO(willchan): Update all unittests so we don't need this.
36TCPSocketParams::TCPSocketParams(const std::string& host, int port,
37                                 RequestPriority priority, const GURL& referrer,
38                                 bool disable_resolver_cache)
39    : destination_(HostPortPair(host, port))
40#ifdef ANDROID
41      , ignore_limits_(false)
42#endif
43    {
44  Initialize(priority, referrer, disable_resolver_cache);
45}
46
47TCPSocketParams::~TCPSocketParams() {}
48
49// TCPConnectJobs will time out after this many seconds.  Note this is the total
50// time, including both host resolution and TCP connect() times.
51//
52// TODO(eroman): The use of this constant needs to be re-evaluated. The time
53// needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
54// the address list may contain many alternatives, and most of those may
55// timeout. Even worse, the per-connect timeout threshold varies greatly
56// between systems (anywhere from 20 seconds to 190 seconds).
57// See comment #12 at http://crbug.com/23364 for specifics.
58static const int kTCPConnectJobTimeoutInSeconds = 240;  // 4 minutes.
59
60TCPConnectJob::TCPConnectJob(
61    const std::string& group_name,
62    const scoped_refptr<TCPSocketParams>& params,
63    base::TimeDelta timeout_duration,
64    ClientSocketFactory* client_socket_factory,
65    HostResolver* host_resolver,
66    Delegate* delegate,
67    NetLog* net_log)
68    : ConnectJob(group_name, timeout_duration, delegate,
69                 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
70      params_(params),
71      client_socket_factory_(client_socket_factory),
72      ALLOW_THIS_IN_INITIALIZER_LIST(
73          callback_(this,
74                    &TCPConnectJob::OnIOComplete)),
75      resolver_(host_resolver) {}
76
77TCPConnectJob::~TCPConnectJob() {
78  // We don't worry about cancelling the host resolution and TCP connect, since
79  // ~SingleRequestHostResolver and ~ClientSocket will take care of it.
80}
81
82LoadState TCPConnectJob::GetLoadState() const {
83  switch (next_state_) {
84    case STATE_RESOLVE_HOST:
85    case STATE_RESOLVE_HOST_COMPLETE:
86      return LOAD_STATE_RESOLVING_HOST;
87    case STATE_TCP_CONNECT:
88    case STATE_TCP_CONNECT_COMPLETE:
89      return LOAD_STATE_CONNECTING;
90    default:
91      NOTREACHED();
92      return LOAD_STATE_IDLE;
93  }
94}
95
96int TCPConnectJob::ConnectInternal() {
97  next_state_ = STATE_RESOLVE_HOST;
98  start_time_ = base::TimeTicks::Now();
99  return DoLoop(OK);
100}
101
102void TCPConnectJob::OnIOComplete(int result) {
103  int rv = DoLoop(result);
104  if (rv != ERR_IO_PENDING)
105    NotifyDelegateOfCompletion(rv);  // Deletes |this|
106}
107
108int TCPConnectJob::DoLoop(int result) {
109  DCHECK_NE(next_state_, STATE_NONE);
110
111  int rv = result;
112  do {
113    State state = next_state_;
114    next_state_ = STATE_NONE;
115    switch (state) {
116      case STATE_RESOLVE_HOST:
117        DCHECK_EQ(OK, rv);
118        rv = DoResolveHost();
119        break;
120      case STATE_RESOLVE_HOST_COMPLETE:
121        rv = DoResolveHostComplete(rv);
122        break;
123      case STATE_TCP_CONNECT:
124        DCHECK_EQ(OK, rv);
125        rv = DoTCPConnect();
126        break;
127      case STATE_TCP_CONNECT_COMPLETE:
128        rv = DoTCPConnectComplete(rv);
129        break;
130      default:
131        NOTREACHED();
132        rv = ERR_FAILED;
133        break;
134    }
135  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
136
137  return rv;
138}
139
140int TCPConnectJob::DoResolveHost() {
141  next_state_ = STATE_RESOLVE_HOST_COMPLETE;
142  return resolver_.Resolve(params_->destination(), &addresses_, &callback_,
143                           net_log());
144}
145
146int TCPConnectJob::DoResolveHostComplete(int result) {
147  if (result == OK)
148    next_state_ = STATE_TCP_CONNECT;
149  return result;
150}
151
152int TCPConnectJob::DoTCPConnect() {
153  next_state_ = STATE_TCP_CONNECT_COMPLETE;
154  set_socket(client_socket_factory_->CreateTCPClientSocket(
155        addresses_, net_log().net_log(), net_log().source()));
156  connect_start_time_ = base::TimeTicks::Now();
157  return socket()->Connect(&callback_,
158#ifdef ANDROID
159                           params_->ignore_limits()
160#endif
161                          );
162}
163
164int TCPConnectJob::DoTCPConnectComplete(int result) {
165  if (result == OK) {
166    DCHECK(connect_start_time_ != base::TimeTicks());
167    DCHECK(start_time_ != base::TimeTicks());
168    base::TimeTicks now = base::TimeTicks::Now();
169    base::TimeDelta total_duration = now - start_time_;
170    UMA_HISTOGRAM_CUSTOM_TIMES(
171        "Net.DNS_Resolution_And_TCP_Connection_Latency2",
172        total_duration,
173        base::TimeDelta::FromMilliseconds(1),
174        base::TimeDelta::FromMinutes(10),
175        100);
176
177    base::TimeDelta connect_duration = now - connect_start_time_;
178    UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
179        connect_duration,
180        base::TimeDelta::FromMilliseconds(1),
181        base::TimeDelta::FromMinutes(10),
182        100);
183  } else {
184    // Delete the socket on error.
185    set_socket(NULL);
186  }
187
188  return result;
189}
190
191ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob(
192    const std::string& group_name,
193    const PoolBase::Request& request,
194    ConnectJob::Delegate* delegate) const {
195  return new TCPConnectJob(group_name, request.params(), ConnectionTimeout(),
196                           client_socket_factory_, host_resolver_, delegate,
197                           net_log_);
198}
199
200base::TimeDelta
201    TCPClientSocketPool::TCPConnectJobFactory::ConnectionTimeout() const {
202  return base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds);
203}
204
205TCPClientSocketPool::TCPClientSocketPool(
206    int max_sockets,
207    int max_sockets_per_group,
208    ClientSocketPoolHistograms* histograms,
209    HostResolver* host_resolver,
210    ClientSocketFactory* client_socket_factory,
211    NetLog* net_log)
212    : base_(max_sockets, max_sockets_per_group, histograms,
213            base::TimeDelta::FromSeconds(
214                ClientSocketPool::unused_idle_socket_timeout()),
215            base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
216            new TCPConnectJobFactory(client_socket_factory,
217                                     host_resolver, net_log)) {
218  base_.EnableConnectBackupJobs();
219}
220
221TCPClientSocketPool::~TCPClientSocketPool() {}
222
223int TCPClientSocketPool::RequestSocket(
224    const std::string& group_name,
225    const void* params,
226    RequestPriority priority,
227    ClientSocketHandle* handle,
228    CompletionCallback* callback,
229    const BoundNetLog& net_log) {
230  const scoped_refptr<TCPSocketParams>* casted_params =
231      static_cast<const scoped_refptr<TCPSocketParams>*>(params);
232
233  if (net_log.IsLoggingAllEvents()) {
234    // TODO(eroman): Split out the host and port parameters.
235    net_log.AddEvent(
236        NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
237        make_scoped_refptr(new NetLogStringParameter(
238            "host_and_port",
239            casted_params->get()->destination().host_port_pair().ToString())));
240  }
241
242  return base_.RequestSocket(group_name, *casted_params, priority, handle,
243                             callback, net_log);
244}
245
246void TCPClientSocketPool::RequestSockets(
247    const std::string& group_name,
248    const void* params,
249    int num_sockets,
250    const BoundNetLog& net_log) {
251  const scoped_refptr<TCPSocketParams>* casted_params =
252      static_cast<const scoped_refptr<TCPSocketParams>*>(params);
253
254  if (net_log.IsLoggingAllEvents()) {
255    // TODO(eroman): Split out the host and port parameters.
256    net_log.AddEvent(
257        NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
258        make_scoped_refptr(new NetLogStringParameter(
259            "host_and_port",
260            casted_params->get()->destination().host_port_pair().ToString())));
261  }
262
263  base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
264}
265
266void TCPClientSocketPool::CancelRequest(
267    const std::string& group_name,
268    ClientSocketHandle* handle) {
269  base_.CancelRequest(group_name, handle);
270}
271
272void TCPClientSocketPool::ReleaseSocket(
273    const std::string& group_name,
274    ClientSocket* socket,
275    int id) {
276  base_.ReleaseSocket(group_name, socket, id);
277}
278
279void TCPClientSocketPool::Flush() {
280  base_.Flush();
281}
282
283void TCPClientSocketPool::CloseIdleSockets() {
284  base_.CloseIdleSockets();
285}
286
287int TCPClientSocketPool::IdleSocketCountInGroup(
288    const std::string& group_name) const {
289  return base_.IdleSocketCountInGroup(group_name);
290}
291
292LoadState TCPClientSocketPool::GetLoadState(
293    const std::string& group_name, const ClientSocketHandle* handle) const {
294  return base_.GetLoadState(group_name, handle);
295}
296
297}  // namespace net
298