blob: 2739aef04f14ecad9ef1f5d198e6d6330cdbc9e2 [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"
[email protected]ea15bd52014-07-14 22:42:5019#include "components/variations/proto/client_variations.pb.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
asvitkine9a279832015-12-18 02:35:5023// static
[email protected]ab7780792013-01-10 01:26:0924VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
olli.raula36aa8be2015-09-10 11:14:2225 return base::Singleton<VariationsHttpHeaderProvider>::get();
[email protected]bd3b4712012-12-18 17:01:3026}
27
asvitkine9a279832015-12-18 02:35:5028std::string VariationsHttpHeaderProvider::GetClientDataHeader() {
[email protected]bd3b4712012-12-18 17:01:3029 // Lazily initialize the header, if not already done, before attempting to
30 // transmit it.
31 InitVariationIDsCacheIfNeeded();
[email protected]ab7780792013-01-10 01:26:0932
33 std::string variation_ids_header_copy;
34 {
35 base::AutoLock scoped_lock(lock_);
36 variation_ids_header_copy = variation_ids_header_;
37 }
asvitkine9a279832015-12-18 02:35:5038 return variation_ids_header_copy;
[email protected]bd3b4712012-12-18 17:01:3039}
40
asvitkine35ba6472015-12-18 23:52:3341std::string VariationsHttpHeaderProvider::GetVariationsString() {
42 InitVariationIDsCacheIfNeeded();
43
44 // Construct a space-separated string with leading and trailing spaces from
45 // the variations set. Note: The ids in it will be in sorted order per the
46 // std::set contract.
47 std::string ids_string = " ";
48 {
49 base::AutoLock scoped_lock(lock_);
50 for (VariationID id : GetAllVariationIds()) {
51 ids_string.append(base::IntToString(id));
52 ids_string.push_back(' ');
53 }
54 }
55 return ids_string;
56}
57
[email protected]1bd918d2013-10-13 18:23:0958bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
59 const std::string& variation_ids) {
60 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2961 default_trigger_id_set_.clear();
brettw8be197d12015-07-23 23:23:3162 for (const base::StringPiece& entry : base::SplitStringPiece(
63 variation_ids, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
64 if (entry.empty()) {
[email protected]1bd918d2013-10-13 18:23:0965 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2966 default_trigger_id_set_.clear();
[email protected]1bd918d2013-10-13 18:23:0967 return false;
68 }
brettw8be197d12015-07-23 23:23:3169 bool trigger_id =
70 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE);
[email protected]8c2c5442014-04-04 18:55:2971 // Remove the "t" prefix if it's there.
asvitkine9a279832015-12-18 02:35:5072 base::StringPiece trimmed_entry = trigger_id ? entry.substr(1) : entry;
[email protected]8c2c5442014-04-04 18:55:2973
74 int variation_id = 0;
brettw8be197d12015-07-23 23:23:3175 if (!base::StringToInt(trimmed_entry, &variation_id)) {
[email protected]8c2c5442014-04-04 18:55:2976 default_variation_ids_set_.clear();
77 default_trigger_id_set_.clear();
78 return false;
79 }
80 if (trigger_id)
81 default_trigger_id_set_.insert(variation_id);
82 else
83 default_variation_ids_set_.insert(variation_id);
[email protected]1bd918d2013-10-13 18:23:0984 }
85 return true;
86}
87
asvitkineb4ed78682015-03-12 18:18:5488void VariationsHttpHeaderProvider::ResetForTesting() {
89 base::AutoLock scoped_lock(lock_);
90
91 // Stop observing field trials so that it can be restarted when this is
92 // re-inited. Note: This is a no-op if this is not currently observing.
93 base::FieldTrialList::RemoveObserver(this);
94 variation_ids_cache_initialized_ = false;
95}
96
[email protected]ab7780792013-01-10 01:26:0997VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
asvitkine9a279832015-12-18 02:35:5098 : variation_ids_cache_initialized_(false) {}
[email protected]bd3b4712012-12-18 17:01:3099
asvitkine9a279832015-12-18 02:35:50100VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {}
[email protected]bd3b4712012-12-18 17:01:30101
[email protected]ab7780792013-01-10 01:26:09102void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
[email protected]bd3b4712012-12-18 17:01:30103 const std::string& trial_name,
104 const std::string& group_name) {
[email protected]ab7780792013-01-10 01:26:09105 VariationID new_id =
106 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
[email protected]e51dcb0c2014-05-06 16:56:10107 VariationID new_trigger_id = GetGoogleVariationID(
108 GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name);
109 if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30110 return;
[email protected]ab7780792013-01-10 01:26:09111
[email protected]bd3b4712012-12-18 17:01:30112 base::AutoLock scoped_lock(lock_);
[email protected]e51dcb0c2014-05-06 16:56:10113 if (new_id != EMPTY_ID)
114 variation_ids_set_.insert(new_id);
115 if (new_trigger_id != EMPTY_ID)
116 variation_trigger_ids_set_.insert(new_trigger_id);
117
[email protected]bd3b4712012-12-18 17:01:30118 UpdateVariationIDsHeaderValue();
119}
120
asvitkinee0dbdbe2014-10-31 21:59:57121void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
asvitkine9a279832015-12-18 02:35:50122 const std::vector<SyntheticTrialGroup>& groups) {
asvitkinee0dbdbe2014-10-31 21:59:57123 base::AutoLock scoped_lock(lock_);
124
125 synthetic_variation_ids_set_.clear();
asvitkine9a279832015-12-18 02:35:50126 for (const SyntheticTrialGroup& group : groups) {
asvitkinee0dbdbe2014-10-31 21:59:57127 const VariationID id =
128 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id);
129 if (id != EMPTY_ID)
130 synthetic_variation_ids_set_.insert(id);
131 }
132 UpdateVariationIDsHeaderValue();
133}
134
[email protected]ab7780792013-01-10 01:26:09135void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
[email protected]bd3b4712012-12-18 17:01:30136 base::AutoLock scoped_lock(lock_);
137 if (variation_ids_cache_initialized_)
138 return;
139
140 // Register for additional cache updates. This is done first to avoid a race
141 // that could cause registered FieldTrials to be missed.
[email protected]b3a25092013-05-28 22:08:16142 DCHECK(base::MessageLoop::current());
[email protected]bd3b4712012-12-18 17:01:30143 base::FieldTrialList::AddObserver(this);
144
[email protected]999f7b42013-02-04 16:14:25145 base::TimeTicks before_time = base::TimeTicks::Now();
146
[email protected]bd3b4712012-12-18 17:01:30147 base::FieldTrial::ActiveGroups initial_groups;
148 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
asvitkine35ba6472015-12-18 23:52:33149
150 for (const auto& entry : initial_groups) {
151 const VariationID id =
152 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, entry.trial_name,
153 entry.group_name);
[email protected]90acad02013-01-16 17:17:54154 if (id != EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30155 variation_ids_set_.insert(id);
[email protected]e51dcb0c2014-05-06 16:56:10156
asvitkine35ba6472015-12-18 23:52:33157 const VariationID trigger_id =
158 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER, entry.trial_name,
159 entry.group_name);
160
[email protected]e51dcb0c2014-05-06 16:56:10161 if (trigger_id != EMPTY_ID)
162 variation_trigger_ids_set_.insert(trigger_id);
[email protected]bd3b4712012-12-18 17:01:30163 }
164 UpdateVariationIDsHeaderValue();
165
[email protected]999f7b42013-02-04 16:14:25166 UMA_HISTOGRAM_CUSTOM_COUNTS(
167 "Variations.HeaderConstructionTime",
asvitkine9a279832015-12-18 02:35:50168 (base::TimeTicks::Now() - before_time).InMicroseconds(), 0,
169 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50);
[email protected]999f7b42013-02-04 16:14:25170
[email protected]bd3b4712012-12-18 17:01:30171 variation_ids_cache_initialized_ = true;
172}
173
[email protected]ab7780792013-01-10 01:26:09174void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
175 lock_.AssertAcquired();
176
[email protected]bd3b4712012-12-18 17:01:30177 // The header value is a serialized protobuffer of Variation IDs which is
178 // base64 encoded before transmitting as a string.
[email protected]1bd918d2013-10-13 18:23:09179 variation_ids_header_.clear();
180
[email protected]8c2c5442014-04-04 18:55:29181 if (variation_ids_set_.empty() && default_variation_ids_set_.empty() &&
asvitkinee0dbdbe2014-10-31 21:59:57182 variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty() &&
183 synthetic_variation_ids_set_.empty()) {
[email protected]bd3b4712012-12-18 17:01:30184 return;
[email protected]8c2c5442014-04-04 18:55:29185 }
[email protected]bd3b4712012-12-18 17:01:30186
187 // This is the bottleneck for the creation of the header, so validate the size
188 // here. Force a hard maximum on the ID count in case the Variations server
189 // returns too many IDs and DOSs receiving servers with large requests.
[email protected]e51dcb0c2014-05-06 16:56:10190 const size_t total_id_count =
191 variation_ids_set_.size() + variation_trigger_ids_set_.size();
192 DCHECK_LE(total_id_count, 10U);
[email protected]a27ae2a2014-08-01 16:17:52193 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
194 total_id_count);
[email protected]e51dcb0c2014-05-06 16:56:10195 if (total_id_count > 20)
[email protected]bd3b4712012-12-18 17:01:30196 return;
[email protected]bd3b4712012-12-18 17:01:30197
asvitkine35ba6472015-12-18 23:52:33198 std::set<VariationID> all_variation_ids_set = GetAllVariationIds();
[email protected]e51dcb0c2014-05-06 16:56:10199 std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_;
asvitkinee0dbdbe2014-10-31 21:59:57200 for (VariationID id : variation_trigger_ids_set_)
201 all_trigger_ids_set.insert(id);
202
203 ClientVariations proto;
204 for (VariationID id : all_variation_ids_set)
205 proto.add_variation_id(id);
206 for (VariationID id : all_trigger_ids_set)
207 proto.add_trigger_variation_id(id);
[email protected]8c2c5442014-04-04 18:55:29208
[email protected]bd3b4712012-12-18 17:01:30209 std::string serialized;
210 proto.SerializeToString(&serialized);
211
212 std::string hashed;
[email protected]33fca122013-12-11 01:48:50213 base::Base64Encode(serialized, &hashed);
214 // If successful, swap the header value with the new one.
215 // Note that the list of IDs and the header could be temporarily out of sync
216 // if IDs are added as the header is recreated. The receiving servers are OK
217 // with such discrepancies.
218 variation_ids_header_ = hashed;
[email protected]bd3b4712012-12-18 17:01:30219}
[email protected]ab7780792013-01-10 01:26:09220
asvitkine35ba6472015-12-18 23:52:33221std::set<VariationID> VariationsHttpHeaderProvider::GetAllVariationIds() {
222 lock_.AssertAcquired();
223
224 std::set<VariationID> all_variation_ids_set = default_variation_ids_set_;
225 for (VariationID id : variation_ids_set_)
226 all_variation_ids_set.insert(id);
227 for (VariationID id : synthetic_variation_ids_set_)
228 all_variation_ids_set.insert(id);
229 return all_variation_ids_set;
230}
231
[email protected]71011c1682014-07-09 17:19:16232} // namespace variations