blob: e23f5581a14b52e4bd3d82e686672dcf829bb5c2 [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
avi5dd91f82015-12-25 22:30:467#include <stddef.h>
8
horoe09b6c82014-11-01 02:08:289#include <set>
10#include <string>
[email protected]1bd918d2013-10-13 18:23:0911#include <vector>
12
[email protected]bd3b4712012-12-18 17:01:3013#include "base/base64.h"
14#include "base/memory/singleton.h"
asvitkine454600f2015-06-16 16:34:5015#include "base/metrics/histogram_macros.h"
[email protected]1bd918d2013-10-13 18:23:0916#include "base/strings/string_number_conversions.h"
17#include "base/strings/string_split.h"
[email protected]7f8a9932013-07-26 20:43:3418#include "base/strings/string_util.h"
fdoraycddcf0e2016-06-23 21:20:3019#include "base/threading/thread_task_runner_handle.h"
[email protected]ea15bd52014-07-14 22:42:5020#include "components/variations/proto/client_variations.pb.h"
[email protected]bd3b4712012-12-18 17:01:3021
[email protected]71011c1682014-07-09 17:19:1622namespace variations {
[email protected]ab7780792013-01-10 01:26:0923
asvitkine9a279832015-12-18 02:35:5024// static
[email protected]ab7780792013-01-10 01:26:0925VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
olli.raula36aa8be2015-09-10 11:14:2226 return base::Singleton<VariationsHttpHeaderProvider>::get();
[email protected]bd3b4712012-12-18 17:01:3027}
28
yutak3305f49d2016-12-13 10:32:3129std::string VariationsHttpHeaderProvider::GetClientDataHeader(
30 bool is_signed_in) {
[email protected]bd3b4712012-12-18 17:01:3031 // Lazily initialize the header, if not already done, before attempting to
32 // transmit it.
33 InitVariationIDsCacheIfNeeded();
[email protected]ab7780792013-01-10 01:26:0934
35 std::string variation_ids_header_copy;
36 {
37 base::AutoLock scoped_lock(lock_);
yutak3305f49d2016-12-13 10:32:3138 variation_ids_header_copy = is_signed_in
39 ? cached_variation_ids_header_signed_in_
40 : cached_variation_ids_header_;
[email protected]ab7780792013-01-10 01:26:0941 }
asvitkine9a279832015-12-18 02:35:5042 return variation_ids_header_copy;
[email protected]bd3b4712012-12-18 17:01:3043}
44
asvitkine35ba6472015-12-18 23:52:3345std::string VariationsHttpHeaderProvider::GetVariationsString() {
46 InitVariationIDsCacheIfNeeded();
47
48 // Construct a space-separated string with leading and trailing spaces from
49 // the variations set. Note: The ids in it will be in sorted order per the
50 // std::set contract.
51 std::string ids_string = " ";
52 {
53 base::AutoLock scoped_lock(lock_);
yutak3305f49d2016-12-13 10:32:3154 for (const VariationIDEntry& entry : GetAllVariationIds()) {
55 if (entry.second == GOOGLE_WEB_PROPERTIES) {
56 ids_string.append(base::IntToString(entry.first));
57 ids_string.push_back(' ');
58 }
asvitkine35ba6472015-12-18 23:52:3359 }
60 }
61 return ids_string;
62}
63
jkrcalbf073372016-07-29 07:21:3164bool VariationsHttpHeaderProvider::ForceVariationIds(
65 const std::string& command_line_variation_ids,
66 std::vector<std::string>* variation_ids) {
67 if (!command_line_variation_ids.empty()) {
68 // Combine |variation_ids| with |command_line_variation_ids|.
69 std::vector<std::string> variation_ids_flags =
70 base::SplitString(command_line_variation_ids, ",",
71 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
72 variation_ids->insert(variation_ids->end(), variation_ids_flags.begin(),
73 variation_ids_flags.end());
74 }
75
76 if (!variation_ids->empty()) {
77 // Create default variation ids which will always be included in the
78 // X-Client-Data request header.
79 return SetDefaultVariationIds(*variation_ids);
80 }
81 return true;
82}
83
[email protected]1bd918d2013-10-13 18:23:0984bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
jkrcalbf073372016-07-29 07:21:3185 const std::vector<std::string>& variation_ids) {
[email protected]1bd918d2013-10-13 18:23:0986 default_variation_ids_set_.clear();
jkrcalbf073372016-07-29 07:21:3187 for (const std::string& entry : variation_ids) {
brettw8be197d12015-07-23 23:23:3188 if (entry.empty()) {
[email protected]1bd918d2013-10-13 18:23:0989 default_variation_ids_set_.clear();
90 return false;
91 }
brettw8be197d12015-07-23 23:23:3192 bool trigger_id =
93 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE);
[email protected]8c2c5442014-04-04 18:55:2994 // Remove the "t" prefix if it's there.
jkrcalbf073372016-07-29 07:21:3195 std::string trimmed_entry = trigger_id ? entry.substr(1) : entry;
[email protected]8c2c5442014-04-04 18:55:2996
97 int variation_id = 0;
brettw8be197d12015-07-23 23:23:3198 if (!base::StringToInt(trimmed_entry, &variation_id)) {
[email protected]8c2c5442014-04-04 18:55:2999 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:29100 return false;
101 }
yutak3305f49d2016-12-13 10:32:31102 default_variation_ids_set_.insert(VariationIDEntry(
103 variation_id,
104 trigger_id ? GOOGLE_WEB_PROPERTIES_TRIGGER : GOOGLE_WEB_PROPERTIES));
[email protected]1bd918d2013-10-13 18:23:09105 }
106 return true;
107}
108
asvitkineb4ed78682015-03-12 18:18:54109void VariationsHttpHeaderProvider::ResetForTesting() {
110 base::AutoLock scoped_lock(lock_);
111
112 // Stop observing field trials so that it can be restarted when this is
113 // re-inited. Note: This is a no-op if this is not currently observing.
114 base::FieldTrialList::RemoveObserver(this);
115 variation_ids_cache_initialized_ = false;
116}
117
[email protected]ab7780792013-01-10 01:26:09118VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
asvitkine9a279832015-12-18 02:35:50119 : variation_ids_cache_initialized_(false) {}
[email protected]bd3b4712012-12-18 17:01:30120
asvitkine9a279832015-12-18 02:35:50121VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {}
[email protected]bd3b4712012-12-18 17:01:30122
[email protected]ab7780792013-01-10 01:26:09123void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
[email protected]bd3b4712012-12-18 17:01:30124 const std::string& trial_name,
125 const std::string& group_name) {
[email protected]bd3b4712012-12-18 17:01:30126 base::AutoLock scoped_lock(lock_);
yutak3305f49d2016-12-13 10:32:31127 const size_t old_size = variation_ids_set_.size();
128 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES);
129 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_SIGNED_IN);
130 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_TRIGGER);
131 if (variation_ids_set_.size() != old_size)
132 UpdateVariationIDsHeaderValue();
[email protected]bd3b4712012-12-18 17:01:30133}
134
asvitkinee0dbdbe2014-10-31 21:59:57135void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
asvitkine9a279832015-12-18 02:35:50136 const std::vector<SyntheticTrialGroup>& groups) {
asvitkinee0dbdbe2014-10-31 21:59:57137 base::AutoLock scoped_lock(lock_);
138
139 synthetic_variation_ids_set_.clear();
asvitkine9a279832015-12-18 02:35:50140 for (const SyntheticTrialGroup& group : groups) {
yutak3305f49d2016-12-13 10:32:31141 VariationID id =
asvitkinee0dbdbe2014-10-31 21:59:57142 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id);
yutak3305f49d2016-12-13 10:32:31143 if (id != EMPTY_ID) {
144 synthetic_variation_ids_set_.insert(
145 VariationIDEntry(id, GOOGLE_WEB_PROPERTIES));
146 }
147 id = GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES_SIGNED_IN,
148 group.id);
149 if (id != EMPTY_ID) {
150 synthetic_variation_ids_set_.insert(
151 VariationIDEntry(id, GOOGLE_WEB_PROPERTIES_SIGNED_IN));
152 }
asvitkinee0dbdbe2014-10-31 21:59:57153 }
154 UpdateVariationIDsHeaderValue();
155}
156
[email protected]ab7780792013-01-10 01:26:09157void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
[email protected]bd3b4712012-12-18 17:01:30158 base::AutoLock scoped_lock(lock_);
159 if (variation_ids_cache_initialized_)
160 return;
161
162 // Register for additional cache updates. This is done first to avoid a race
163 // that could cause registered FieldTrials to be missed.
fdoraycddcf0e2016-06-23 21:20:30164 DCHECK(base::ThreadTaskRunnerHandle::IsSet());
[email protected]bd3b4712012-12-18 17:01:30165 base::FieldTrialList::AddObserver(this);
166
[email protected]999f7b42013-02-04 16:14:25167 base::TimeTicks before_time = base::TimeTicks::Now();
168
[email protected]bd3b4712012-12-18 17:01:30169 base::FieldTrial::ActiveGroups initial_groups;
170 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
asvitkine35ba6472015-12-18 23:52:33171
172 for (const auto& entry : initial_groups) {
yutak3305f49d2016-12-13 10:32:31173 CacheVariationsId(entry.trial_name, entry.group_name,
174 GOOGLE_WEB_PROPERTIES);
175 CacheVariationsId(entry.trial_name, entry.group_name,
176 GOOGLE_WEB_PROPERTIES_SIGNED_IN);
177 CacheVariationsId(entry.trial_name, entry.group_name,
178 GOOGLE_WEB_PROPERTIES_TRIGGER);
[email protected]bd3b4712012-12-18 17:01:30179 }
180 UpdateVariationIDsHeaderValue();
181
[email protected]999f7b42013-02-04 16:14:25182 UMA_HISTOGRAM_CUSTOM_COUNTS(
183 "Variations.HeaderConstructionTime",
drbasicf0d1b262016-08-23 06:10:42184 (base::TimeTicks::Now() - before_time).InMicroseconds(), 1,
asvitkine9a279832015-12-18 02:35:50185 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50);
[email protected]999f7b42013-02-04 16:14:25186
[email protected]bd3b4712012-12-18 17:01:30187 variation_ids_cache_initialized_ = true;
188}
189
yutak3305f49d2016-12-13 10:32:31190void VariationsHttpHeaderProvider::CacheVariationsId(
191 const std::string& trial_name,
192 const std::string& group_name,
193 IDCollectionKey key) {
194 const VariationID id = GetGoogleVariationID(key, trial_name, group_name);
195 if (id != EMPTY_ID)
196 variation_ids_set_.insert(VariationIDEntry(id, key));
197}
198
[email protected]ab7780792013-01-10 01:26:09199void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
200 lock_.AssertAcquired();
201
[email protected]bd3b4712012-12-18 17:01:30202 // The header value is a serialized protobuffer of Variation IDs which is
203 // base64 encoded before transmitting as a string.
yutak3305f49d2016-12-13 10:32:31204 cached_variation_ids_header_.clear();
205 cached_variation_ids_header_signed_in_.clear();
[email protected]1bd918d2013-10-13 18:23:09206
yutak3305f49d2016-12-13 10:32:31207 // If successful, swap the header value with the new one.
208 // Note that the list of IDs and the header could be temporarily out of sync
209 // if IDs are added as the header is recreated. The receiving servers are OK
210 // with such discrepancies.
211 cached_variation_ids_header_ = GenerateBase64EncodedProto(false);
212 cached_variation_ids_header_signed_in_ = GenerateBase64EncodedProto(true);
213}
214
215std::string VariationsHttpHeaderProvider::GenerateBase64EncodedProto(
216 bool is_signed_in) {
217 std::set<VariationIDEntry> all_variation_ids_set = GetAllVariationIds();
218
219 ClientVariations proto;
220 for (const VariationIDEntry& entry : all_variation_ids_set) {
221 switch (entry.second) {
222 case GOOGLE_WEB_PROPERTIES_SIGNED_IN:
223 if (is_signed_in)
224 proto.add_variation_id(entry.first);
225 break;
226 case GOOGLE_WEB_PROPERTIES:
227 proto.add_variation_id(entry.first);
228 break;
229 case GOOGLE_WEB_PROPERTIES_TRIGGER:
230 proto.add_trigger_variation_id(entry.first);
231 break;
Sky Malicea846ad7d2017-12-05 00:44:42232 case CHROME_SYNC_EVENT_LOGGER:
yutak3305f49d2016-12-13 10:32:31233 case ID_COLLECTION_COUNT:
234 // These cases included to get full enum coverage for switch, so that
235 // new enums introduce compiler warnings. Nothing to do for these.
236 break;
237 }
[email protected]8c2c5442014-04-04 18:55:29238 }
[email protected]bd3b4712012-12-18 17:01:30239
yutak3305f49d2016-12-13 10:32:31240 const size_t total_id_count =
241 proto.variation_id_size() + proto.trigger_variation_id_size();
242
243 if (total_id_count == 0)
244 return std::string();
245
[email protected]bd3b4712012-12-18 17:01:30246 // This is the bottleneck for the creation of the header, so validate the size
247 // here. Force a hard maximum on the ID count in case the Variations server
248 // returns too many IDs and DOSs receiving servers with large requests.
[email protected]e51dcb0c2014-05-06 16:56:10249 DCHECK_LE(total_id_count, 10U);
[email protected]a27ae2a2014-08-01 16:17:52250 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
251 total_id_count);
[email protected]e51dcb0c2014-05-06 16:56:10252 if (total_id_count > 20)
yutak3305f49d2016-12-13 10:32:31253 return std::string();
[email protected]8c2c5442014-04-04 18:55:29254
[email protected]bd3b4712012-12-18 17:01:30255 std::string serialized;
256 proto.SerializeToString(&serialized);
257
258 std::string hashed;
[email protected]33fca122013-12-11 01:48:50259 base::Base64Encode(serialized, &hashed);
yutak3305f49d2016-12-13 10:32:31260 return hashed;
[email protected]bd3b4712012-12-18 17:01:30261}
[email protected]ab7780792013-01-10 01:26:09262
yutak3305f49d2016-12-13 10:32:31263std::set<VariationsHttpHeaderProvider::VariationIDEntry>
264VariationsHttpHeaderProvider::GetAllVariationIds() {
asvitkine35ba6472015-12-18 23:52:33265 lock_.AssertAcquired();
266
yutak3305f49d2016-12-13 10:32:31267 std::set<VariationIDEntry> all_variation_ids_set = default_variation_ids_set_;
268 for (const VariationIDEntry& entry : variation_ids_set_) {
269 all_variation_ids_set.insert(entry);
270 }
271 for (const VariationIDEntry& entry : synthetic_variation_ids_set_) {
272 all_variation_ids_set.insert(entry);
273 }
asvitkine35ba6472015-12-18 23:52:33274 return all_variation_ids_set;
275}
276
[email protected]71011c1682014-07-09 17:19:16277} // namespace variations