blob: 97f96b03e2e2a4e5d316666d19c098a9f27491a7 [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
asvitkine9a279832015-12-18 02:35:505#include "components/variations/variations_http_header_provider.h"
[email protected]bd3b4712012-12-18 17:01:306
horoe09b6c82014-11-01 02:08:287#include <set>
8#include <string>
[email protected]1bd918d2013-10-13 18:23:099#include <vector>
10
[email protected]bd3b4712012-12-18 17:01:3011#include "base/base64.h"
12#include "base/memory/singleton.h"
asvitkine454600f2015-06-16 16:34:5013#include "base/metrics/histogram_macros.h"
[email protected]1bd918d2013-10-13 18:23:0914#include "base/strings/string_number_conversions.h"
15#include "base/strings/string_split.h"
[email protected]7f8a9932013-07-26 20:43:3416#include "base/strings/string_util.h"
[email protected]ea15bd52014-07-14 22:42:5017#include "components/variations/proto/client_variations.pb.h"
[email protected]bd3b4712012-12-18 17:01:3018
[email protected]71011c1682014-07-09 17:19:1619namespace variations {
[email protected]ab7780792013-01-10 01:26:0920
asvitkine9a279832015-12-18 02:35:5021// static
[email protected]ab7780792013-01-10 01:26:0922VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
olli.raula36aa8be2015-09-10 11:14:2223 return base::Singleton<VariationsHttpHeaderProvider>::get();
[email protected]bd3b4712012-12-18 17:01:3024}
25
asvitkine9a279832015-12-18 02:35:5026std::string VariationsHttpHeaderProvider::GetClientDataHeader() {
[email protected]bd3b4712012-12-18 17:01:3027 // Lazily initialize the header, if not already done, before attempting to
28 // transmit it.
29 InitVariationIDsCacheIfNeeded();
[email protected]ab7780792013-01-10 01:26:0930
31 std::string variation_ids_header_copy;
32 {
33 base::AutoLock scoped_lock(lock_);
34 variation_ids_header_copy = variation_ids_header_;
35 }
asvitkine9a279832015-12-18 02:35:5036 return variation_ids_header_copy;
[email protected]bd3b4712012-12-18 17:01:3037}
38
asvitkine35ba6472015-12-18 23:52:3339std::string VariationsHttpHeaderProvider::GetVariationsString() {
40 InitVariationIDsCacheIfNeeded();
41
42 // Construct a space-separated string with leading and trailing spaces from
43 // the variations set. Note: The ids in it will be in sorted order per the
44 // std::set contract.
45 std::string ids_string = " ";
46 {
47 base::AutoLock scoped_lock(lock_);
48 for (VariationID id : GetAllVariationIds()) {
49 ids_string.append(base::IntToString(id));
50 ids_string.push_back(' ');
51 }
52 }
53 return ids_string;
54}
55
[email protected]1bd918d2013-10-13 18:23:0956bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
57 const std::string& variation_ids) {
58 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2959 default_trigger_id_set_.clear();
brettw8be197d12015-07-23 23:23:3160 for (const base::StringPiece& entry : base::SplitStringPiece(
61 variation_ids, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
62 if (entry.empty()) {
[email protected]1bd918d2013-10-13 18:23:0963 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2964 default_trigger_id_set_.clear();
[email protected]1bd918d2013-10-13 18:23:0965 return false;
66 }
brettw8be197d12015-07-23 23:23:3167 bool trigger_id =
68 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE);
[email protected]8c2c5442014-04-04 18:55:2969 // Remove the "t" prefix if it's there.
asvitkine9a279832015-12-18 02:35:5070 base::StringPiece trimmed_entry = trigger_id ? entry.substr(1) : entry;
[email protected]8c2c5442014-04-04 18:55:2971
72 int variation_id = 0;
brettw8be197d12015-07-23 23:23:3173 if (!base::StringToInt(trimmed_entry, &variation_id)) {
[email protected]8c2c5442014-04-04 18:55:2974 default_variation_ids_set_.clear();
75 default_trigger_id_set_.clear();
76 return false;
77 }
78 if (trigger_id)
79 default_trigger_id_set_.insert(variation_id);
80 else
81 default_variation_ids_set_.insert(variation_id);
[email protected]1bd918d2013-10-13 18:23:0982 }
83 return true;
84}
85
asvitkineb4ed78682015-03-12 18:18:5486void VariationsHttpHeaderProvider::ResetForTesting() {
87 base::AutoLock scoped_lock(lock_);
88
89 // Stop observing field trials so that it can be restarted when this is
90 // re-inited. Note: This is a no-op if this is not currently observing.
91 base::FieldTrialList::RemoveObserver(this);
92 variation_ids_cache_initialized_ = false;
93}
94
[email protected]ab7780792013-01-10 01:26:0995VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
asvitkine9a279832015-12-18 02:35:5096 : variation_ids_cache_initialized_(false) {}
[email protected]bd3b4712012-12-18 17:01:3097
asvitkine9a279832015-12-18 02:35:5098VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {}
[email protected]bd3b4712012-12-18 17:01:3099
[email protected]ab7780792013-01-10 01:26:09100void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
[email protected]bd3b4712012-12-18 17:01:30101 const std::string& trial_name,
102 const std::string& group_name) {
[email protected]ab7780792013-01-10 01:26:09103 VariationID new_id =
104 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
[email protected]e51dcb0c2014-05-06 16:56:10105 VariationID new_trigger_id = GetGoogleVariationID(
106 GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name);
107 if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30108 return;
[email protected]ab7780792013-01-10 01:26:09109
[email protected]bd3b4712012-12-18 17:01:30110 base::AutoLock scoped_lock(lock_);
[email protected]e51dcb0c2014-05-06 16:56:10111 if (new_id != EMPTY_ID)
112 variation_ids_set_.insert(new_id);
113 if (new_trigger_id != EMPTY_ID)
114 variation_trigger_ids_set_.insert(new_trigger_id);
115
[email protected]bd3b4712012-12-18 17:01:30116 UpdateVariationIDsHeaderValue();
117}
118
asvitkinee0dbdbe2014-10-31 21:59:57119void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
asvitkine9a279832015-12-18 02:35:50120 const std::vector<SyntheticTrialGroup>& groups) {
asvitkinee0dbdbe2014-10-31 21:59:57121 base::AutoLock scoped_lock(lock_);
122
123 synthetic_variation_ids_set_.clear();
asvitkine9a279832015-12-18 02:35:50124 for (const SyntheticTrialGroup& group : groups) {
asvitkinee0dbdbe2014-10-31 21:59:57125 const VariationID id =
126 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id);
127 if (id != EMPTY_ID)
128 synthetic_variation_ids_set_.insert(id);
129 }
130 UpdateVariationIDsHeaderValue();
131}
132
[email protected]ab7780792013-01-10 01:26:09133void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
[email protected]bd3b4712012-12-18 17:01:30134 base::AutoLock scoped_lock(lock_);
135 if (variation_ids_cache_initialized_)
136 return;
137
138 // Register for additional cache updates. This is done first to avoid a race
139 // that could cause registered FieldTrials to be missed.
[email protected]b3a25092013-05-28 22:08:16140 DCHECK(base::MessageLoop::current());
[email protected]bd3b4712012-12-18 17:01:30141 base::FieldTrialList::AddObserver(this);
142
[email protected]999f7b42013-02-04 16:14:25143 base::TimeTicks before_time = base::TimeTicks::Now();
144
[email protected]bd3b4712012-12-18 17:01:30145 base::FieldTrial::ActiveGroups initial_groups;
146 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
asvitkine35ba6472015-12-18 23:52:33147
148 for (const auto& entry : initial_groups) {
149 const VariationID id =
150 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, entry.trial_name,
151 entry.group_name);
[email protected]90acad02013-01-16 17:17:54152 if (id != EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30153 variation_ids_set_.insert(id);
[email protected]e51dcb0c2014-05-06 16:56:10154
asvitkine35ba6472015-12-18 23:52:33155 const VariationID trigger_id =
156 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER, entry.trial_name,
157 entry.group_name);
158
[email protected]e51dcb0c2014-05-06 16:56:10159 if (trigger_id != EMPTY_ID)
160 variation_trigger_ids_set_.insert(trigger_id);
[email protected]bd3b4712012-12-18 17:01:30161 }
162 UpdateVariationIDsHeaderValue();
163
[email protected]999f7b42013-02-04 16:14:25164 UMA_HISTOGRAM_CUSTOM_COUNTS(
165 "Variations.HeaderConstructionTime",
asvitkine9a279832015-12-18 02:35:50166 (base::TimeTicks::Now() - before_time).InMicroseconds(), 0,
167 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50);
[email protected]999f7b42013-02-04 16:14:25168
[email protected]bd3b4712012-12-18 17:01:30169 variation_ids_cache_initialized_ = true;
170}
171
[email protected]ab7780792013-01-10 01:26:09172void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
173 lock_.AssertAcquired();
174
[email protected]bd3b4712012-12-18 17:01:30175 // The header value is a serialized protobuffer of Variation IDs which is
176 // base64 encoded before transmitting as a string.
[email protected]1bd918d2013-10-13 18:23:09177 variation_ids_header_.clear();
178
[email protected]8c2c5442014-04-04 18:55:29179 if (variation_ids_set_.empty() && default_variation_ids_set_.empty() &&
asvitkinee0dbdbe2014-10-31 21:59:57180 variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty() &&
181 synthetic_variation_ids_set_.empty()) {
[email protected]bd3b4712012-12-18 17:01:30182 return;
[email protected]8c2c5442014-04-04 18:55:29183 }
[email protected]bd3b4712012-12-18 17:01:30184
185 // This is the bottleneck for the creation of the header, so validate the size
186 // here. Force a hard maximum on the ID count in case the Variations server
187 // returns too many IDs and DOSs receiving servers with large requests.
[email protected]e51dcb0c2014-05-06 16:56:10188 const size_t total_id_count =
189 variation_ids_set_.size() + variation_trigger_ids_set_.size();
190 DCHECK_LE(total_id_count, 10U);
[email protected]a27ae2a2014-08-01 16:17:52191 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
192 total_id_count);
[email protected]e51dcb0c2014-05-06 16:56:10193 if (total_id_count > 20)
[email protected]bd3b4712012-12-18 17:01:30194 return;
[email protected]bd3b4712012-12-18 17:01:30195
asvitkine35ba6472015-12-18 23:52:33196 std::set<VariationID> all_variation_ids_set = GetAllVariationIds();
[email protected]e51dcb0c2014-05-06 16:56:10197 std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_;
asvitkinee0dbdbe2014-10-31 21:59:57198 for (VariationID id : variation_trigger_ids_set_)
199 all_trigger_ids_set.insert(id);
200
201 ClientVariations proto;
202 for (VariationID id : all_variation_ids_set)
203 proto.add_variation_id(id);
204 for (VariationID id : all_trigger_ids_set)
205 proto.add_trigger_variation_id(id);
[email protected]8c2c5442014-04-04 18:55:29206
[email protected]bd3b4712012-12-18 17:01:30207 std::string serialized;
208 proto.SerializeToString(&serialized);
209
210 std::string hashed;
[email protected]33fca122013-12-11 01:48:50211 base::Base64Encode(serialized, &hashed);
212 // If successful, swap the header value with the new one.
213 // Note that the list of IDs and the header could be temporarily out of sync
214 // if IDs are added as the header is recreated. The receiving servers are OK
215 // with such discrepancies.
216 variation_ids_header_ = hashed;
[email protected]bd3b4712012-12-18 17:01:30217}
[email protected]ab7780792013-01-10 01:26:09218
asvitkine35ba6472015-12-18 23:52:33219std::set<VariationID> VariationsHttpHeaderProvider::GetAllVariationIds() {
220 lock_.AssertAcquired();
221
222 std::set<VariationID> all_variation_ids_set = default_variation_ids_set_;
223 for (VariationID id : variation_ids_set_)
224 all_variation_ids_set.insert(id);
225 for (VariationID id : synthetic_variation_ids_set_)
226 all_variation_ids_set.insert(id);
227 return all_variation_ids_set;
228}
229
[email protected]71011c1682014-07-09 17:19:16230} // namespace variations