Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / metrics / variations / variations_http_header_provider.cc
blob08907f3b8d42e5b4f0b87382afb3433615f5493c
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.
5 #include "chrome/browser/metrics/variations/variations_http_header_provider.h"
7 #include <vector>
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"
21 namespace chrome_variations {
23 VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
24 return Singleton<VariationsHttpHeaderProvider>::get();
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;
41 if (uma_enabled)
42 headers->SetHeaderIfMissing("X-Chrome-UMA-Enabled", "1");
44 // Lazily initialize the header, if not already done, before attempting to
45 // transmit it.
46 InitVariationIDsCacheIfNeeded();
48 std::string variation_ids_header_copy;
50 base::AutoLock scoped_lock(lock_);
51 variation_ids_header_copy = variation_ids_header_;
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);
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;
73 default_variation_ids_set_.insert(variation_id);
75 return true;
78 VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
79 : variation_ids_cache_initialized_(false) {
82 VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {
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;
93 base::AutoLock scoped_lock(lock_);
94 variation_ids_set_.insert(new_id);
95 UpdateVariationIDsHeaderValue();
98 void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
99 base::AutoLock scoped_lock(lock_);
100 if (variation_ids_cache_initialized_)
101 return;
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);
108 base::TimeTicks before_time = base::TimeTicks::Now();
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);
121 UpdateVariationIDsHeaderValue();
123 UMA_HISTOGRAM_CUSTOM_COUNTS(
124 "Variations.HeaderConstructionTime",
125 (base::TimeTicks::Now() - before_time).InMicroseconds(),
127 base::TimeDelta::FromSeconds(1).InMicroseconds(),
128 50);
130 variation_ids_cache_initialized_ = true;
133 void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
134 lock_.AssertAcquired();
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();
140 if (variation_ids_set_.empty() && default_variation_ids_set_.empty())
141 return;
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;
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);
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);
162 std::string serialized;
163 proto.SerializeToString(&serialized);
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;
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;
181 // The below mirrors logic in IsGoogleDomainUrl(), but for youtube.<TLD>.
182 if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS())
183 return false;
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;
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);
198 } // namespace chrome_variations