blob: 12d9c9f4e365aafcae659da51fbfadf451314ce8 [file] [log] [blame]
[email protected]71011c1682014-07-09 17:19:161// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]bd3b4712012-12-18 17:01:302// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]71011c1682014-07-09 17:19:165#include "components/variations/variations_http_header_provider.h"
[email protected]bd3b4712012-12-18 17:01:306
[email protected]1bd918d2013-10-13 18:23:097#include <vector>
8
[email protected]bd3b4712012-12-18 17:01:309#include "base/base64.h"
10#include "base/memory/singleton.h"
[email protected]999f7b42013-02-04 16:14:2511#include "base/metrics/histogram.h"
[email protected]1bd918d2013-10-13 18:23:0912#include "base/strings/string_number_conversions.h"
13#include "base/strings/string_split.h"
[email protected]7f8a9932013-07-26 20:43:3414#include "base/strings/string_util.h"
[email protected]8e44a5b02014-06-19 19:03:2415#include "components/google/core/browser/google_util.h"
[email protected]ea15bd52014-07-14 22:42:5016#include "components/variations/proto/client_variations.pb.h"
[email protected]7f8a9932013-07-26 20:43:3417#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
[email protected]bd3b4712012-12-18 17:01:3018#include "net/http/http_request_headers.h"
[email protected]7f8a9932013-07-26 20:43:3419#include "url/gurl.h"
[email protected]bd3b4712012-12-18 17:01:3020
[email protected]71011c1682014-07-09 17:19:1621namespace variations {
[email protected]ab7780792013-01-10 01:26:0922
[email protected]64d617e2014-05-31 04:37:5423namespace {
24
25const char* kSuffixesToSetHeadersFor[] = {
26 ".android.com",
27 ".doubleclick.com",
28 ".doubleclick.net",
29 ".ggpht.com",
30 ".googleadservices.com",
31 ".googleapis.com",
32 ".googlesyndication.com",
33 ".googleusercontent.com",
34 ".googlevideo.com",
35 ".gstatic.com",
36 ".ytimg.com",
37};
38
39} // namespace
40
[email protected]ab7780792013-01-10 01:26:0941VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
42 return Singleton<VariationsHttpHeaderProvider>::get();
[email protected]bd3b4712012-12-18 17:01:3043}
44
[email protected]ab7780792013-01-10 01:26:0945void VariationsHttpHeaderProvider::AppendHeaders(
46 const GURL& url,
47 bool incognito,
48 bool uma_enabled,
49 net::HttpRequestHeaders* headers) {
[email protected]ea15bd52014-07-14 22:42:5050 // Note the criteria for attaching client experiment headers:
[email protected]0eea1cf72014-04-01 19:39:1951 // 1. We only transmit to Google owned domains which can evaluate experiments.
52 // 1a. These include hosts which have a standard postfix such as:
53 // *.doubleclick.net or *.googlesyndication.com or
54 // exactly www.googleadservices.com or
55 // international TLD domains *.google.<TLD> or *.youtube.<TLD>.
[email protected]bd3b4712012-12-18 17:01:3056 // 2. Only transmit for non-Incognito profiles.
57 // 3. For the X-Chrome-UMA-Enabled bit, only set it if UMA is in fact enabled
58 // for this install of Chrome.
[email protected]05ef1f22013-12-05 22:24:3459 // 4. For the X-Client-Data header, only include non-empty variation IDs.
[email protected]7f8a9932013-07-26 20:43:3460 if (incognito || !ShouldAppendHeaders(url))
[email protected]bd3b4712012-12-18 17:01:3061 return;
[email protected]bd3b4712012-12-18 17:01:3062
63 if (uma_enabled)
64 headers->SetHeaderIfMissing("X-Chrome-UMA-Enabled", "1");
65
66 // Lazily initialize the header, if not already done, before attempting to
67 // transmit it.
68 InitVariationIDsCacheIfNeeded();
[email protected]ab7780792013-01-10 01:26:0969
70 std::string variation_ids_header_copy;
71 {
72 base::AutoLock scoped_lock(lock_);
73 variation_ids_header_copy = variation_ids_header_;
74 }
75
76 if (!variation_ids_header_copy.empty()) {
[email protected]05ef1f22013-12-05 22:24:3477 // Note that prior to M33 this header was named X-Chrome-Variations.
78 headers->SetHeaderIfMissing("X-Client-Data",
[email protected]ab7780792013-01-10 01:26:0979 variation_ids_header_copy);
80 }
[email protected]bd3b4712012-12-18 17:01:3081}
82
[email protected]1bd918d2013-10-13 18:23:0983bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
84 const std::string& variation_ids) {
85 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2986 default_trigger_id_set_.clear();
[email protected]1bd918d2013-10-13 18:23:0987 std::vector<std::string> entries;
88 base::SplitString(variation_ids, ',', &entries);
89 for (std::vector<std::string>::const_iterator it = entries.begin();
90 it != entries.end(); ++it) {
[email protected]8c2c5442014-04-04 18:55:2991 if (it->empty()) {
[email protected]1bd918d2013-10-13 18:23:0992 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2993 default_trigger_id_set_.clear();
[email protected]1bd918d2013-10-13 18:23:0994 return false;
95 }
[email protected]8c2c5442014-04-04 18:55:2996 bool trigger_id = StartsWithASCII(*it, "t", true);
97 // Remove the "t" prefix if it's there.
98 std::string entry = trigger_id ? it->substr(1) : *it;
99
100 int variation_id = 0;
101 if (!base::StringToInt(entry, &variation_id)) {
102 default_variation_ids_set_.clear();
103 default_trigger_id_set_.clear();
104 return false;
105 }
106 if (trigger_id)
107 default_trigger_id_set_.insert(variation_id);
108 else
109 default_variation_ids_set_.insert(variation_id);
[email protected]1bd918d2013-10-13 18:23:09110 }
111 return true;
112}
113
[email protected]ab7780792013-01-10 01:26:09114VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
[email protected]bd3b4712012-12-18 17:01:30115 : variation_ids_cache_initialized_(false) {
116}
117
[email protected]ab7780792013-01-10 01:26:09118VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {
[email protected]bd3b4712012-12-18 17:01:30119}
120
[email protected]ab7780792013-01-10 01:26:09121void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
[email protected]bd3b4712012-12-18 17:01:30122 const std::string& trial_name,
123 const std::string& group_name) {
[email protected]ab7780792013-01-10 01:26:09124 VariationID new_id =
125 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
[email protected]e51dcb0c2014-05-06 16:56:10126 VariationID new_trigger_id = GetGoogleVariationID(
127 GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name);
128 if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30129 return;
[email protected]ab7780792013-01-10 01:26:09130
[email protected]bd3b4712012-12-18 17:01:30131 base::AutoLock scoped_lock(lock_);
[email protected]e51dcb0c2014-05-06 16:56:10132 if (new_id != EMPTY_ID)
133 variation_ids_set_.insert(new_id);
134 if (new_trigger_id != EMPTY_ID)
135 variation_trigger_ids_set_.insert(new_trigger_id);
136
[email protected]bd3b4712012-12-18 17:01:30137 UpdateVariationIDsHeaderValue();
138}
139
[email protected]ab7780792013-01-10 01:26:09140void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
[email protected]bd3b4712012-12-18 17:01:30141 base::AutoLock scoped_lock(lock_);
142 if (variation_ids_cache_initialized_)
143 return;
144
145 // Register for additional cache updates. This is done first to avoid a race
146 // that could cause registered FieldTrials to be missed.
[email protected]b3a25092013-05-28 22:08:16147 DCHECK(base::MessageLoop::current());
[email protected]bd3b4712012-12-18 17:01:30148 base::FieldTrialList::AddObserver(this);
149
[email protected]999f7b42013-02-04 16:14:25150 base::TimeTicks before_time = base::TimeTicks::Now();
151
[email protected]bd3b4712012-12-18 17:01:30152 base::FieldTrial::ActiveGroups initial_groups;
153 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
154 for (base::FieldTrial::ActiveGroups::const_iterator it =
[email protected]ab7780792013-01-10 01:26:09155 initial_groups.begin();
156 it != initial_groups.end(); ++it) {
157 const VariationID id =
158 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, it->trial_name,
159 it->group_name);
[email protected]90acad02013-01-16 17:17:54160 if (id != EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30161 variation_ids_set_.insert(id);
[email protected]e51dcb0c2014-05-06 16:56:10162
163 const VariationID trigger_id =
164 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER, it->trial_name,
165 it->group_name);
166 if (trigger_id != EMPTY_ID)
167 variation_trigger_ids_set_.insert(trigger_id);
[email protected]bd3b4712012-12-18 17:01:30168 }
169 UpdateVariationIDsHeaderValue();
170
[email protected]999f7b42013-02-04 16:14:25171 UMA_HISTOGRAM_CUSTOM_COUNTS(
172 "Variations.HeaderConstructionTime",
173 (base::TimeTicks::Now() - before_time).InMicroseconds(),
174 0,
175 base::TimeDelta::FromSeconds(1).InMicroseconds(),
176 50);
177
[email protected]bd3b4712012-12-18 17:01:30178 variation_ids_cache_initialized_ = true;
179}
180
[email protected]ab7780792013-01-10 01:26:09181void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
182 lock_.AssertAcquired();
183
[email protected]bd3b4712012-12-18 17:01:30184 // The header value is a serialized protobuffer of Variation IDs which is
185 // base64 encoded before transmitting as a string.
[email protected]1bd918d2013-10-13 18:23:09186 variation_ids_header_.clear();
187
[email protected]8c2c5442014-04-04 18:55:29188 if (variation_ids_set_.empty() && default_variation_ids_set_.empty() &&
[email protected]e51dcb0c2014-05-06 16:56:10189 variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty()) {
[email protected]bd3b4712012-12-18 17:01:30190 return;
[email protected]8c2c5442014-04-04 18:55:29191 }
[email protected]bd3b4712012-12-18 17:01:30192
193 // This is the bottleneck for the creation of the header, so validate the size
194 // here. Force a hard maximum on the ID count in case the Variations server
195 // returns too many IDs and DOSs receiving servers with large requests.
[email protected]e51dcb0c2014-05-06 16:56:10196 const size_t total_id_count =
197 variation_ids_set_.size() + variation_trigger_ids_set_.size();
198 DCHECK_LE(total_id_count, 10U);
[email protected]a27ae2a2014-08-01 16:17:52199 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
200 total_id_count);
[email protected]e51dcb0c2014-05-06 16:56:10201 if (total_id_count > 20)
[email protected]bd3b4712012-12-18 17:01:30202 return;
[email protected]bd3b4712012-12-18 17:01:30203
[email protected]1bd918d2013-10-13 18:23:09204 // Merge the two sets of experiment ids.
205 std::set<VariationID> all_variation_ids_set = default_variation_ids_set_;
[email protected]ab7780792013-01-10 01:26:09206 for (std::set<VariationID>::const_iterator it = variation_ids_set_.begin();
207 it != variation_ids_set_.end(); ++it) {
[email protected]1bd918d2013-10-13 18:23:09208 all_variation_ids_set.insert(*it);
209 }
[email protected]ea15bd52014-07-14 22:42:50210 ClientVariations proto;
[email protected]1bd918d2013-10-13 18:23:09211 for (std::set<VariationID>::const_iterator it = all_variation_ids_set.begin();
212 it != all_variation_ids_set.end(); ++it) {
[email protected]bd3b4712012-12-18 17:01:30213 proto.add_variation_id(*it);
[email protected]ab7780792013-01-10 01:26:09214 }
[email protected]bd3b4712012-12-18 17:01:30215
[email protected]e51dcb0c2014-05-06 16:56:10216 std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_;
[email protected]8c2c5442014-04-04 18:55:29217 for (std::set<VariationID>::const_iterator it =
[email protected]e51dcb0c2014-05-06 16:56:10218 variation_trigger_ids_set_.begin();
219 it != variation_trigger_ids_set_.end(); ++it) {
220 all_trigger_ids_set.insert(*it);
221 }
222 for (std::set<VariationID>::const_iterator it = all_trigger_ids_set.begin();
223 it != all_trigger_ids_set.end(); ++it) {
[email protected]8c2c5442014-04-04 18:55:29224 proto.add_trigger_variation_id(*it);
225 }
226
[email protected]bd3b4712012-12-18 17:01:30227 std::string serialized;
228 proto.SerializeToString(&serialized);
229
230 std::string hashed;
[email protected]33fca122013-12-11 01:48:50231 base::Base64Encode(serialized, &hashed);
232 // If successful, swap the header value with the new one.
233 // Note that the list of IDs and the header could be temporarily out of sync
234 // if IDs are added as the header is recreated. The receiving servers are OK
235 // with such discrepancies.
236 variation_ids_header_ = hashed;
[email protected]bd3b4712012-12-18 17:01:30237}
[email protected]ab7780792013-01-10 01:26:09238
[email protected]7f8a9932013-07-26 20:43:34239// static
240bool VariationsHttpHeaderProvider::ShouldAppendHeaders(const GURL& url) {
241 if (google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN,
242 google_util::ALLOW_NON_STANDARD_PORTS)) {
243 return true;
244 }
245
[email protected]91f5689032013-08-22 01:43:33246 if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS())
[email protected]7f8a9932013-07-26 20:43:34247 return false;
248
[email protected]0eea1cf72014-04-01 19:39:19249 // Some domains don't have international TLD extensions, so testing for them
250 // is very straight forward.
[email protected]7f8a9932013-07-26 20:43:34251 const std::string host = url.host();
[email protected]64d617e2014-05-31 04:37:54252 for (size_t i = 0; i < arraysize(kSuffixesToSetHeadersFor); ++i) {
253 if (EndsWith(host, kSuffixesToSetHeadersFor[i], false))
254 return true;
[email protected]0eea1cf72014-04-01 19:39:19255 }
256
[email protected]87abe9ea2014-06-17 03:29:54257 return google_util::IsYoutubeDomainUrl(url, google_util::ALLOW_SUBDOMAIN,
258 google_util::ALLOW_NON_STANDARD_PORTS);
[email protected]7f8a9932013-07-26 20:43:34259}
260
[email protected]71011c1682014-07-09 17:19:16261} // namespace variations