Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / metrics / variations / variations_http_header_provider.cc
1 // Copyright (c) 2012 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 "chrome/browser/metrics/variations/variations_http_header_provider.h"
6
7 #include <vector>
8
9 #include "base/base64.h"
10 #include "base/memory/singleton.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "chrome/browser/google/google_util.h"
16 #include "chrome/common/metrics/proto/chrome_experiments.pb.h"
17 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
18 #include "net/http/http_request_headers.h"
19 #include "url/gurl.h"
20
21 namespace chrome_variations {
22
23 VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
24   return Singleton<VariationsHttpHeaderProvider>::get();
25 }
26
27 void VariationsHttpHeaderProvider::AppendHeaders(
28     const GURL& url,
29     bool incognito,
30     bool uma_enabled,
31     net::HttpRequestHeaders* headers) {
32   // Note the criteria for attaching Chrome experiment headers:
33   // 1. We only transmit to *.google.<TLD> or *.youtube.<TLD> domains.
34   // 2. Only transmit for non-Incognito profiles.
35   // 3. For the X-Chrome-UMA-Enabled bit, only set it if UMA is in fact enabled
36   //    for this install of Chrome.
37   // 4. For the X-Client-Data header, only include non-empty variation IDs.
38   if (incognito || !ShouldAppendHeaders(url))
39     return;
40
41   if (uma_enabled)
42     headers->SetHeaderIfMissing("X-Chrome-UMA-Enabled", "1");
43
44   // Lazily initialize the header, if not already done, before attempting to
45   // transmit it.
46   InitVariationIDsCacheIfNeeded();
47
48   std::string variation_ids_header_copy;
49   {
50     base::AutoLock scoped_lock(lock_);
51     variation_ids_header_copy = variation_ids_header_;
52   }
53
54   if (!variation_ids_header_copy.empty()) {
55     // Note that prior to M33 this header was named X-Chrome-Variations.
56     headers->SetHeaderIfMissing("X-Client-Data",
57                                 variation_ids_header_copy);
58   }
59 }
60
61 bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
62     const std::string& variation_ids) {
63   default_variation_ids_set_.clear();
64   std::vector<std::string> entries;
65   base::SplitString(variation_ids, ',', &entries);
66   for (std::vector<std::string>::const_iterator it = entries.begin();
67        it != entries.end(); ++it) {
68     int variation_id = 0;
69     if (!base::StringToInt(*it, &variation_id)) {
70       default_variation_ids_set_.clear();
71       return false;
72     }
73     default_variation_ids_set_.insert(variation_id);
74   }
75   return true;
76 }
77
78 VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
79     : variation_ids_cache_initialized_(false) {
80 }
81
82 VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {
83 }
84
85 void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
86     const std::string& trial_name,
87     const std::string& group_name) {
88   VariationID new_id =
89       GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
90   if (new_id == EMPTY_ID)
91     return;
92
93   base::AutoLock scoped_lock(lock_);
94   variation_ids_set_.insert(new_id);
95   UpdateVariationIDsHeaderValue();
96 }
97
98 void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
99   base::AutoLock scoped_lock(lock_);
100   if (variation_ids_cache_initialized_)
101     return;
102
103   // Register for additional cache updates. This is done first to avoid a race
104   // that could cause registered FieldTrials to be missed.
105   DCHECK(base::MessageLoop::current());
106   base::FieldTrialList::AddObserver(this);
107
108   base::TimeTicks before_time = base::TimeTicks::Now();
109
110   base::FieldTrial::ActiveGroups initial_groups;
111   base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
112   for (base::FieldTrial::ActiveGroups::const_iterator it =
113            initial_groups.begin();
114        it != initial_groups.end(); ++it) {
115     const VariationID id =
116         GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, it->trial_name,
117                              it->group_name);
118     if (id != EMPTY_ID)
119       variation_ids_set_.insert(id);
120   }
121   UpdateVariationIDsHeaderValue();
122
123   UMA_HISTOGRAM_CUSTOM_COUNTS(
124       "Variations.HeaderConstructionTime",
125       (base::TimeTicks::Now() - before_time).InMicroseconds(),
126       0,
127       base::TimeDelta::FromSeconds(1).InMicroseconds(),
128       50);
129
130   variation_ids_cache_initialized_ = true;
131 }
132
133 void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
134   lock_.AssertAcquired();
135
136   // The header value is a serialized protobuffer of Variation IDs which is
137   // base64 encoded before transmitting as a string.
138   variation_ids_header_.clear();
139
140   if (variation_ids_set_.empty() && default_variation_ids_set_.empty())
141     return;
142
143   // This is the bottleneck for the creation of the header, so validate the size
144   // here. Force a hard maximum on the ID count in case the Variations server
145   // returns too many IDs and DOSs receiving servers with large requests.
146   DCHECK_LE(variation_ids_set_.size(), 10U);
147   if (variation_ids_set_.size() > 20)
148     return;
149
150   // Merge the two sets of experiment ids.
151   std::set<VariationID> all_variation_ids_set = default_variation_ids_set_;
152   for (std::set<VariationID>::const_iterator it = variation_ids_set_.begin();
153        it != variation_ids_set_.end(); ++it) {
154     all_variation_ids_set.insert(*it);
155   }
156   metrics::ChromeVariations proto;
157   for (std::set<VariationID>::const_iterator it = all_variation_ids_set.begin();
158        it != all_variation_ids_set.end(); ++it) {
159     proto.add_variation_id(*it);
160   }
161
162   std::string serialized;
163   proto.SerializeToString(&serialized);
164
165   std::string hashed;
166   base::Base64Encode(serialized, &hashed);
167   // If successful, swap the header value with the new one.
168   // Note that the list of IDs and the header could be temporarily out of sync
169   // if IDs are added as the header is recreated. The receiving servers are OK
170   // with such discrepancies.
171   variation_ids_header_ = hashed;
172 }
173
174 // static
175 bool VariationsHttpHeaderProvider::ShouldAppendHeaders(const GURL& url) {
176   if (google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN,
177                                      google_util::ALLOW_NON_STANDARD_PORTS)) {
178     return true;
179   }
180
181   // The below mirrors logic in IsGoogleDomainUrl(), but for youtube.<TLD>.
182   if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS())
183     return false;
184
185   const std::string host = url.host();
186   const size_t tld_length = net::registry_controlled_domains::GetRegistryLength(
187       host,
188       net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
189       net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
190   if ((tld_length == 0) || (tld_length == std::string::npos))
191     return false;
192
193   const std::string host_minus_tld(host, 0, host.length() - tld_length);
194   return LowerCaseEqualsASCII(host_minus_tld, "youtube.") ||
195       EndsWith(host_minus_tld, ".youtube.", false);
196 }
197
198 }  // namespace chrome_variations