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/client_socket.h"
6
7#include "base/metrics/field_trial.h"
8#include "base/metrics/histogram.h"
9#include "base/string_number_conversions.h"
10#include "base/values.h"
11
12namespace net {
13
14namespace {
15
16// Parameters for SOCKET_BYTES_RECEIVED and SOCKET_BYTES_SENT events.
17// Includes bytes transferred and, if |bytes| is not NULL, the bytes themselves.
18class NetLogBytesTransferredParameter : public NetLog::EventParameters {
19 public:
20  NetLogBytesTransferredParameter(int byte_count, const char* bytes);
21
22  virtual Value* ToValue() const;
23
24 private:
25  const int byte_count_;
26  std::string hex_encoded_bytes_;
27  bool has_bytes_;
28};
29
30NetLogBytesTransferredParameter::NetLogBytesTransferredParameter(
31    int byte_count, const char* transferred_bytes)
32    : byte_count_(byte_count),
33      has_bytes_(false) {
34  if (transferred_bytes) {
35    hex_encoded_bytes_ = base::HexEncode(transferred_bytes, byte_count);
36    has_bytes_ = true;
37  }
38}
39
40Value* NetLogBytesTransferredParameter::ToValue() const {
41  DictionaryValue* dict = new DictionaryValue();
42  dict->SetInteger("byte_count", byte_count_);
43  if (has_bytes_)
44    dict->SetString("hex_encoded_bytes", hex_encoded_bytes_);
45  return dict;
46}
47
48}  // namespace
49
50ClientSocket::UseHistory::UseHistory()
51    : was_ever_connected_(false),
52      was_used_to_convey_data_(false),
53      omnibox_speculation_(false),
54      subresource_speculation_(false) {
55}
56
57ClientSocket::UseHistory::~UseHistory() {
58  EmitPreconnectionHistograms();
59}
60
61void ClientSocket::UseHistory::Reset() {
62  EmitPreconnectionHistograms();
63  was_ever_connected_ = false;
64  was_used_to_convey_data_ = false;
65  // omnibox_speculation_ and subresource_speculation_ values
66  // are intentionally preserved.
67}
68
69void ClientSocket::UseHistory::set_was_ever_connected() {
70  DCHECK(!was_used_to_convey_data_);
71  was_ever_connected_ = true;
72}
73
74void ClientSocket::UseHistory::set_was_used_to_convey_data() {
75  DCHECK(was_ever_connected_);
76  was_used_to_convey_data_ = true;
77}
78
79
80void ClientSocket::UseHistory::set_subresource_speculation() {
81  DCHECK(was_ever_connected_);
82  // TODO(jar): We should transition to marking a socket (or stream) at
83  // construction time as being created for speculative reasons.  This current
84  // approach of trying to track use of a socket to convey data can make
85  // mistakes when other sockets (such as ones sitting in the pool for a long
86  // time) are issued.  Unused sockets can be left over when a when a set of
87  // connections to a host are made, and one is "unlucky" and takes so long to
88  // complete a connection, that another socket is used, and recycled before a
89  // second connection comes available.  Similarly, re-try connections can leave
90  // an original (slow to connect socket) in the pool, and that can be issued
91  // to a speculative requester. In any cases such old sockets will fail when an
92  // attempt is made to used them!... and then it will look like a speculative
93  // socket was discarded without any user!?!?!
94  if (was_used_to_convey_data_)
95    return;
96  subresource_speculation_ = true;
97}
98
99void ClientSocket::UseHistory::set_omnibox_speculation() {
100  DCHECK(was_ever_connected_);
101  if (was_used_to_convey_data_)
102    return;
103  omnibox_speculation_ = true;
104}
105
106bool ClientSocket::UseHistory::was_used_to_convey_data() const {
107  DCHECK(!was_used_to_convey_data_ || was_ever_connected_);
108  return was_used_to_convey_data_;
109}
110
111void ClientSocket::UseHistory::EmitPreconnectionHistograms() const {
112  DCHECK(!subresource_speculation_ || !omnibox_speculation_);
113  // 0 ==> non-speculative, never connected.
114  // 1 ==> non-speculative never used (but connected).
115  // 2 ==> non-speculative and used.
116  // 3 ==> omnibox_speculative never connected.
117  // 4 ==> omnibox_speculative never used (but connected).
118  // 5 ==> omnibox_speculative and used.
119  // 6 ==> subresource_speculative never connected.
120  // 7 ==> subresource_speculative never used (but connected).
121  // 8 ==> subresource_speculative and used.
122  int result;
123  if (was_used_to_convey_data_)
124    result = 2;
125  else if (was_ever_connected_)
126    result = 1;
127  else
128    result = 0;  // Never used, and not really connected.
129
130  if (omnibox_speculation_)
131    result += 3;
132  else if (subresource_speculation_)
133    result += 6;
134  UMA_HISTOGRAM_ENUMERATION("Net.PreconnectUtilization2", result, 9);
135
136  static const bool connect_backup_jobs_fieldtrial =
137      base::FieldTrialList::Find("ConnnectBackupJobs") &&
138      !base::FieldTrialList::Find("ConnnectBackupJobs")->group_name().empty();
139  if (connect_backup_jobs_fieldtrial) {
140    UMA_HISTOGRAM_ENUMERATION(
141        base::FieldTrial::MakeName("Net.PreconnectUtilization2",
142                                   "ConnnectBackupJobs"),
143        result, 9);
144  }
145}
146
147void ClientSocket::LogByteTransfer(const BoundNetLog& net_log,
148                                   NetLog::EventType event_type,
149                                   int byte_count,
150                                   char* bytes) const {
151  scoped_refptr<NetLog::EventParameters> params;
152  if (net_log.IsLoggingBytes()) {
153    params = new NetLogBytesTransferredParameter(byte_count, bytes);
154  } else {
155    params = new NetLogBytesTransferredParameter(byte_count, NULL);
156  }
157  net_log.AddEvent(event_type, params);
158}
159
160}  // namespace net
161