blob: d77a4bf8e3d024de175152d6e74580b836cb47e0 [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
asvitkine9a279832015-12-18 02:35:5029std::string VariationsHttpHeaderProvider::GetClientDataHeader() {
[email protected]bd3b4712012-12-18 17:01:3030 // Lazily initialize the header, if not already done, before attempting to
31 // transmit it.
32 InitVariationIDsCacheIfNeeded();
[email protected]ab7780792013-01-10 01:26:0933
34 std::string variation_ids_header_copy;
35 {
36 base::AutoLock scoped_lock(lock_);
37 variation_ids_header_copy = variation_ids_header_;
38 }
asvitkine9a279832015-12-18 02:35:5039 return variation_ids_header_copy;
[email protected]bd3b4712012-12-18 17:01:3040}
41
asvitkine35ba6472015-12-18 23:52:3342std::string VariationsHttpHeaderProvider::GetVariationsString() {
43 InitVariationIDsCacheIfNeeded();
44
45 // Construct a space-separated string with leading and trailing spaces from
46 // the variations set. Note: The ids in it will be in sorted order per the
47 // std::set contract.
48 std::string ids_string = " ";
49 {
50 base::AutoLock scoped_lock(lock_);
51 for (VariationID id : GetAllVariationIds()) {
52 ids_string.append(base::IntToString(id));
53 ids_string.push_back(' ');
54 }
55 }
56 return ids_string;
57}
58
[email protected]1bd918d2013-10-13 18:23:0959bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
60 const std::string& variation_ids) {
61 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2962 default_trigger_id_set_.clear();
brettw8be197d12015-07-23 23:23:3163 for (const base::StringPiece& entry : base::SplitStringPiece(
64 variation_ids, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
65 if (entry.empty()) {
[email protected]1bd918d2013-10-13 18:23:0966 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2967 default_trigger_id_set_.clear();
[email protected]1bd918d2013-10-13 18:23:0968 return false;
69 }
brettw8be197d12015-07-23 23:23:3170 bool trigger_id =
71 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE);
[email protected]8c2c5442014-04-04 18:55:2972 // Remove the "t" prefix if it's there.
asvitkine9a279832015-12-18 02:35:5073 base::StringPiece trimmed_entry = trigger_id ? entry.substr(1) : entry;
[email protected]8c2c5442014-04-04 18:55:2974
75 int variation_id = 0;
brettw8be197d12015-07-23 23:23:3176 if (!base::StringToInt(trimmed_entry, &variation_id)) {
[email protected]8c2c5442014-04-04 18:55:2977 default_variation_ids_set_.clear();
78 default_trigger_id_set_.clear();
79 return false;
80 }
81 if (trigger_id)
82 default_trigger_id_set_.insert(variation_id);
83 else
84 default_variation_ids_set_.insert(variation_id);
[email protected]1bd918d2013-10-13 18:23:0985 }
86 return true;
87}
88
asvitkineb4ed78682015-03-12 18:18:5489void VariationsHttpHeaderProvider::ResetForTesting() {
90 base::AutoLock scoped_lock(lock_);
91
92 // Stop observing field trials so that it can be restarted when this is
93 // re-inited. Note: This is a no-op if this is not currently observing.
94 base::FieldTrialList::RemoveObserver(this);
95 variation_ids_cache_initialized_ = false;
96}
97
[email protected]ab7780792013-01-10 01:26:0998VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
asvitkine9a279832015-12-18 02:35:5099 : variation_ids_cache_initialized_(false) {}
[email protected]bd3b4712012-12-18 17:01:30100
asvitkine9a279832015-12-18 02:35:50101VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {}
[email protected]bd3b4712012-12-18 17:01:30102
[email protected]ab7780792013-01-10 01:26:09103void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
[email protected]bd3b4712012-12-18 17:01:30104 const std::string& trial_name,
105 const std::string& group_name) {
[email protected]ab7780792013-01-10 01:26:09106 VariationID new_id =
107 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
[email protected]e51dcb0c2014-05-06 16:56:10108 VariationID new_trigger_id = GetGoogleVariationID(
109 GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name);
110 if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30111 return;
[email protected]ab7780792013-01-10 01:26:09112
[email protected]bd3b4712012-12-18 17:01:30113 base::AutoLock scoped_lock(lock_);
[email protected]e51dcb0c2014-05-06 16:56:10114 if (new_id != EMPTY_ID)
115 variation_ids_set_.insert(new_id);
116 if (new_trigger_id != EMPTY_ID)
117 variation_trigger_ids_set_.insert(new_trigger_id);
118
[email protected]bd3b4712012-12-18 17:01:30119 UpdateVariationIDsHeaderValue();
120}
121
asvitkinee0dbdbe2014-10-31 21:59:57122void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
asvitkine9a279832015-12-18 02:35:50123 const std::vector<SyntheticTrialGroup>& groups) {
asvitkinee0dbdbe2014-10-31 21:59:57124 base::AutoLock scoped_lock(lock_);
125
126 synthetic_variation_ids_set_.clear();
asvitkine9a279832015-12-18 02:35:50127 for (const SyntheticTrialGroup& group : groups) {
asvitkinee0dbdbe2014-10-31 21:59:57128 const VariationID id =
129 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id);
130 if (id != EMPTY_ID)
131 synthetic_variation_ids_set_.insert(id);
132 }
133 UpdateVariationIDsHeaderValue();
134}
135
[email protected]ab7780792013-01-10 01:26:09136void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
[email protected]bd3b4712012-12-18 17:01:30137 base::AutoLock scoped_lock(lock_);
138 if (variation_ids_cache_initialized_)
139 return;
140
141 // Register for additional cache updates. This is done first to avoid a race
142 // that could cause registered FieldTrials to be missed.
fdoraycddcf0e2016-06-23 21:20:30143 DCHECK(base::ThreadTaskRunnerHandle::IsSet());
[email protected]bd3b4712012-12-18 17:01:30144 base::FieldTrialList::AddObserver(this);
145
[email protected]999f7b42013-02-04 16:14:25146 base::TimeTicks before_time = base::TimeTicks::Now();
147
[email protected]bd3b4712012-12-18 17:01:30148 base::FieldTrial::ActiveGroups initial_groups;
149 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
asvitkine35ba6472015-12-18 23:52:33150
151 for (const auto& entry : initial_groups) {
152 const VariationID id =
153 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, entry.trial_name,
154 entry.group_name);
[email protected]90acad02013-01-16 17:17:54155 if (id != EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30156 variation_ids_set_.insert(id);
[email protected]e51dcb0c2014-05-06 16:56:10157
asvitkine35ba6472015-12-18 23:52:33158 const VariationID trigger_id =
159 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER, entry.trial_name,
160 entry.group_name);
161
[email protected]e51dcb0c2014-05-06 16:56:10162 if (trigger_id != EMPTY_ID)
163 variation_trigger_ids_set_.insert(trigger_id);
[email protected]bd3b4712012-12-18 17:01:30164 }
165 UpdateVariationIDsHeaderValue();
166
[email protected]999f7b42013-02-04 16:14:25167 UMA_HISTOGRAM_CUSTOM_COUNTS(
168 "Variations.HeaderConstructionTime",
asvitkine9a279832015-12-18 02:35:50169 (base::TimeTicks::Now() - before_time).InMicroseconds(), 0,
170 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50);
[email protected]999f7b42013-02-04 16:14:25171
[email protected]bd3b4712012-12-18 17:01:30172 variation_ids_cache_initialized_ = true;
173}
174
[email protected]ab7780792013-01-10 01:26:09175void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
176 lock_.AssertAcquired();
177
[email protected]bd3b4712012-12-18 17:01:30178 // The header value is a serialized protobuffer of Variation IDs which is
179 // base64 encoded before transmitting as a string.
[email protected]1bd918d2013-10-13 18:23:09180 variation_ids_header_.clear();
181
[email protected]8c2c5442014-04-04 18:55:29182 if (variation_ids_set_.empty() && default_variation_ids_set_.empty() &&
asvitkinee0dbdbe2014-10-31 21:59:57183 variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty() &&
184 synthetic_variation_ids_set_.empty()) {
[email protected]bd3b4712012-12-18 17:01:30185 return;
[email protected]8c2c5442014-04-04 18:55:29186 }
[email protected]bd3b4712012-12-18 17:01:30187
188 // This is the bottleneck for the creation of the header, so validate the size
189 // here. Force a hard maximum on the ID count in case the Variations server
190 // returns too many IDs and DOSs receiving servers with large requests.
[email protected]e51dcb0c2014-05-06 16:56:10191 const size_t total_id_count =
192 variation_ids_set_.size() + variation_trigger_ids_set_.size();
193 DCHECK_LE(total_id_count, 10U);
[email protected]a27ae2a2014-08-01 16:17:52194 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
195 total_id_count);
[email protected]e51dcb0c2014-05-06 16:56:10196 if (total_id_count > 20)
[email protected]bd3b4712012-12-18 17:01:30197 return;
[email protected]bd3b4712012-12-18 17:01:30198
asvitkine35ba6472015-12-18 23:52:33199 std::set<VariationID> all_variation_ids_set = GetAllVariationIds();
[email protected]e51dcb0c2014-05-06 16:56:10200 std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_;
asvitkinee0dbdbe2014-10-31 21:59:57201 for (VariationID id : variation_trigger_ids_set_)
202 all_trigger_ids_set.insert(id);
203
204 ClientVariations proto;
205 for (VariationID id : all_variation_ids_set)
206 proto.add_variation_id(id);
207 for (VariationID id : all_trigger_ids_set)
208 proto.add_trigger_variation_id(id);
[email protected]8c2c5442014-04-04 18:55:29209
[email protected]bd3b4712012-12-18 17:01:30210 std::string serialized;
211 proto.SerializeToString(&serialized);
212
213 std::string hashed;
[email protected]33fca122013-12-11 01:48:50214 base::Base64Encode(serialized, &hashed);
215 // If successful, swap the header value with the new one.
216 // Note that the list of IDs and the header could be temporarily out of sync
217 // if IDs are added as the header is recreated. The receiving servers are OK
218 // with such discrepancies.
219 variation_ids_header_ = hashed;
[email protected]bd3b4712012-12-18 17:01:30220}
[email protected]ab7780792013-01-10 01:26:09221
asvitkine35ba6472015-12-18 23:52:33222std::set<VariationID> VariationsHttpHeaderProvider::GetAllVariationIds() {
223 lock_.AssertAcquired();
224
225 std::set<VariationID> all_variation_ids_set = default_variation_ids_set_;
226 for (VariationID id : variation_ids_set_)
227 all_variation_ids_set.insert(id);
228 for (VariationID id : synthetic_variation_ids_set_)
229 all_variation_ids_set.insert(id);
230 return all_variation_ids_set;
231}
232
[email protected]71011c1682014-07-09 17:19:16233} // namespace variations