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