blob: 316fe521dc633ea5afc006c1676c9ea61445ee96 [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
[email protected]1bd918d2013-10-13 18:23:0939bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
40 const std::string& variation_ids) {
41 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2942 default_trigger_id_set_.clear();
brettw8be197d12015-07-23 23:23:3143 for (const base::StringPiece& entry : base::SplitStringPiece(
44 variation_ids, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
45 if (entry.empty()) {
[email protected]1bd918d2013-10-13 18:23:0946 default_variation_ids_set_.clear();
[email protected]8c2c5442014-04-04 18:55:2947 default_trigger_id_set_.clear();
[email protected]1bd918d2013-10-13 18:23:0948 return false;
49 }
brettw8be197d12015-07-23 23:23:3150 bool trigger_id =
51 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE);
[email protected]8c2c5442014-04-04 18:55:2952 // Remove the "t" prefix if it's there.
asvitkine9a279832015-12-18 02:35:5053 base::StringPiece trimmed_entry = trigger_id ? entry.substr(1) : entry;
[email protected]8c2c5442014-04-04 18:55:2954
55 int variation_id = 0;
brettw8be197d12015-07-23 23:23:3156 if (!base::StringToInt(trimmed_entry, &variation_id)) {
[email protected]8c2c5442014-04-04 18:55:2957 default_variation_ids_set_.clear();
58 default_trigger_id_set_.clear();
59 return false;
60 }
61 if (trigger_id)
62 default_trigger_id_set_.insert(variation_id);
63 else
64 default_variation_ids_set_.insert(variation_id);
[email protected]1bd918d2013-10-13 18:23:0965 }
66 return true;
67}
68
asvitkineb4ed78682015-03-12 18:18:5469void VariationsHttpHeaderProvider::ResetForTesting() {
70 base::AutoLock scoped_lock(lock_);
71
72 // Stop observing field trials so that it can be restarted when this is
73 // re-inited. Note: This is a no-op if this is not currently observing.
74 base::FieldTrialList::RemoveObserver(this);
75 variation_ids_cache_initialized_ = false;
76}
77
[email protected]ab7780792013-01-10 01:26:0978VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
asvitkine9a279832015-12-18 02:35:5079 : variation_ids_cache_initialized_(false) {}
[email protected]bd3b4712012-12-18 17:01:3080
asvitkine9a279832015-12-18 02:35:5081VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {}
[email protected]bd3b4712012-12-18 17:01:3082
[email protected]ab7780792013-01-10 01:26:0983void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
[email protected]bd3b4712012-12-18 17:01:3084 const std::string& trial_name,
85 const std::string& group_name) {
[email protected]ab7780792013-01-10 01:26:0986 VariationID new_id =
87 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
[email protected]e51dcb0c2014-05-06 16:56:1088 VariationID new_trigger_id = GetGoogleVariationID(
89 GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name);
90 if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:3091 return;
[email protected]ab7780792013-01-10 01:26:0992
[email protected]bd3b4712012-12-18 17:01:3093 base::AutoLock scoped_lock(lock_);
[email protected]e51dcb0c2014-05-06 16:56:1094 if (new_id != EMPTY_ID)
95 variation_ids_set_.insert(new_id);
96 if (new_trigger_id != EMPTY_ID)
97 variation_trigger_ids_set_.insert(new_trigger_id);
98
[email protected]bd3b4712012-12-18 17:01:3099 UpdateVariationIDsHeaderValue();
100}
101
asvitkinee0dbdbe2014-10-31 21:59:57102void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
asvitkine9a279832015-12-18 02:35:50103 const std::vector<SyntheticTrialGroup>& groups) {
asvitkinee0dbdbe2014-10-31 21:59:57104 base::AutoLock scoped_lock(lock_);
105
106 synthetic_variation_ids_set_.clear();
asvitkine9a279832015-12-18 02:35:50107 for (const SyntheticTrialGroup& group : groups) {
asvitkinee0dbdbe2014-10-31 21:59:57108 const VariationID id =
109 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id);
110 if (id != EMPTY_ID)
111 synthetic_variation_ids_set_.insert(id);
112 }
113 UpdateVariationIDsHeaderValue();
114}
115
[email protected]ab7780792013-01-10 01:26:09116void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
[email protected]bd3b4712012-12-18 17:01:30117 base::AutoLock scoped_lock(lock_);
118 if (variation_ids_cache_initialized_)
119 return;
120
121 // Register for additional cache updates. This is done first to avoid a race
122 // that could cause registered FieldTrials to be missed.
[email protected]b3a25092013-05-28 22:08:16123 DCHECK(base::MessageLoop::current());
[email protected]bd3b4712012-12-18 17:01:30124 base::FieldTrialList::AddObserver(this);
125
[email protected]999f7b42013-02-04 16:14:25126 base::TimeTicks before_time = base::TimeTicks::Now();
127
[email protected]bd3b4712012-12-18 17:01:30128 base::FieldTrial::ActiveGroups initial_groups;
129 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
130 for (base::FieldTrial::ActiveGroups::const_iterator it =
[email protected]ab7780792013-01-10 01:26:09131 initial_groups.begin();
132 it != initial_groups.end(); ++it) {
asvitkine9a279832015-12-18 02:35:50133 const VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES,
134 it->trial_name, it->group_name);
[email protected]90acad02013-01-16 17:17:54135 if (id != EMPTY_ID)
[email protected]bd3b4712012-12-18 17:01:30136 variation_ids_set_.insert(id);
[email protected]e51dcb0c2014-05-06 16:56:10137
asvitkine9a279832015-12-18 02:35:50138 const VariationID trigger_id = GetGoogleVariationID(
139 GOOGLE_WEB_PROPERTIES_TRIGGER, it->trial_name, it->group_name);
[email protected]e51dcb0c2014-05-06 16:56:10140 if (trigger_id != EMPTY_ID)
141 variation_trigger_ids_set_.insert(trigger_id);
[email protected]bd3b4712012-12-18 17:01:30142 }
143 UpdateVariationIDsHeaderValue();
144
[email protected]999f7b42013-02-04 16:14:25145 UMA_HISTOGRAM_CUSTOM_COUNTS(
146 "Variations.HeaderConstructionTime",
asvitkine9a279832015-12-18 02:35:50147 (base::TimeTicks::Now() - before_time).InMicroseconds(), 0,
148 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50);
[email protected]999f7b42013-02-04 16:14:25149
[email protected]bd3b4712012-12-18 17:01:30150 variation_ids_cache_initialized_ = true;
151}
152
[email protected]ab7780792013-01-10 01:26:09153void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
154 lock_.AssertAcquired();
155
[email protected]bd3b4712012-12-18 17:01:30156 // The header value is a serialized protobuffer of Variation IDs which is
157 // base64 encoded before transmitting as a string.
[email protected]1bd918d2013-10-13 18:23:09158 variation_ids_header_.clear();
159
[email protected]8c2c5442014-04-04 18:55:29160 if (variation_ids_set_.empty() && default_variation_ids_set_.empty() &&
asvitkinee0dbdbe2014-10-31 21:59:57161 variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty() &&
162 synthetic_variation_ids_set_.empty()) {
[email protected]bd3b4712012-12-18 17:01:30163 return;
[email protected]8c2c5442014-04-04 18:55:29164 }
[email protected]bd3b4712012-12-18 17:01:30165
166 // This is the bottleneck for the creation of the header, so validate the size
167 // here. Force a hard maximum on the ID count in case the Variations server
168 // returns too many IDs and DOSs receiving servers with large requests.
[email protected]e51dcb0c2014-05-06 16:56:10169 const size_t total_id_count =
170 variation_ids_set_.size() + variation_trigger_ids_set_.size();
171 DCHECK_LE(total_id_count, 10U);
[email protected]a27ae2a2014-08-01 16:17:52172 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
173 total_id_count);
[email protected]e51dcb0c2014-05-06 16:56:10174 if (total_id_count > 20)
[email protected]bd3b4712012-12-18 17:01:30175 return;
[email protected]bd3b4712012-12-18 17:01:30176
[email protected]1bd918d2013-10-13 18:23:09177 // Merge the two sets of experiment ids.
178 std::set<VariationID> all_variation_ids_set = default_variation_ids_set_;
asvitkinee0dbdbe2014-10-31 21:59:57179 for (VariationID id : variation_ids_set_)
180 all_variation_ids_set.insert(id);
181 for (VariationID id : synthetic_variation_ids_set_)
182 all_variation_ids_set.insert(id);
[email protected]bd3b4712012-12-18 17:01:30183
[email protected]e51dcb0c2014-05-06 16:56:10184 std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_;
asvitkinee0dbdbe2014-10-31 21:59:57185 for (VariationID id : variation_trigger_ids_set_)
186 all_trigger_ids_set.insert(id);
187
188 ClientVariations proto;
189 for (VariationID id : all_variation_ids_set)
190 proto.add_variation_id(id);
191 for (VariationID id : all_trigger_ids_set)
192 proto.add_trigger_variation_id(id);
[email protected]8c2c5442014-04-04 18:55:29193
[email protected]bd3b4712012-12-18 17:01:30194 std::string serialized;
195 proto.SerializeToString(&serialized);
196
197 std::string hashed;
[email protected]33fca122013-12-11 01:48:50198 base::Base64Encode(serialized, &hashed);
199 // If successful, swap the header value with the new one.
200 // Note that the list of IDs and the header could be temporarily out of sync
201 // if IDs are added as the header is recreated. The receiving servers are OK
202 // with such discrepancies.
203 variation_ids_header_ = hashed;
[email protected]bd3b4712012-12-18 17:01:30204}
[email protected]ab7780792013-01-10 01:26:09205
[email protected]71011c1682014-07-09 17:19:16206} // namespace variations