| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/variations/variations_http_header_provider.h" | 5 #include "components/variations/variations_http_header_provider.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <set> | 9 #include <set> |
| 10 #include <string> | 10 #include <string> |
| 11 #include <vector> | 11 #include <vector> |
| 12 | 12 |
| 13 #include "base/base64.h" | 13 #include "base/base64.h" |
| 14 #include "base/memory/singleton.h" | 14 #include "base/memory/singleton.h" |
| 15 #include "base/metrics/histogram_macros.h" | 15 #include "base/metrics/histogram_macros.h" |
| 16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
| 17 #include "base/strings/string_split.h" | 17 #include "base/strings/string_split.h" |
| 18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 19 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
| 20 #include "components/variations/proto/client_variations.pb.h" | 20 #include "components/variations/proto/client_variations.pb.h" |
| 21 | 21 |
| 22 namespace variations { | 22 namespace variations { |
| 23 | 23 |
| 24 // static | 24 // static |
| 25 VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() { | 25 VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() { |
| 26 return base::Singleton<VariationsHttpHeaderProvider>::get(); | 26 return base::Singleton<VariationsHttpHeaderProvider>::get(); |
| 27 } | 27 } |
| 28 | 28 |
| 29 std::string VariationsHttpHeaderProvider::GetClientDataHeader( | 29 std::string VariationsHttpHeaderProvider::GetClientDataHeader() { |
| 30 bool is_signed_in) { | |
| 31 // Lazily initialize the header, if not already done, before attempting to | 30 // Lazily initialize the header, if not already done, before attempting to |
| 32 // transmit it. | 31 // transmit it. |
| 33 InitVariationIDsCacheIfNeeded(); | 32 InitVariationIDsCacheIfNeeded(); |
| 34 | 33 |
| 35 std::string variation_ids_header_copy; | 34 std::string variation_ids_header_copy; |
| 36 { | 35 { |
| 37 base::AutoLock scoped_lock(lock_); | 36 base::AutoLock scoped_lock(lock_); |
| 38 variation_ids_header_copy = is_signed_in | 37 variation_ids_header_copy = variation_ids_header_; |
| 39 ? cached_variation_ids_header_signed_in_ | |
| 40 : cached_variation_ids_header_; | |
| 41 } | 38 } |
| 42 return variation_ids_header_copy; | 39 return variation_ids_header_copy; |
| 43 } | 40 } |
| 44 | 41 |
| 45 std::string VariationsHttpHeaderProvider::GetVariationsString() { | 42 std::string VariationsHttpHeaderProvider::GetVariationsString() { |
| 46 InitVariationIDsCacheIfNeeded(); | 43 InitVariationIDsCacheIfNeeded(); |
| 47 | 44 |
| 48 // Construct a space-separated string with leading and trailing spaces from | 45 // 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 | 46 // the variations set. Note: The ids in it will be in sorted order per the |
| 50 // std::set contract. | 47 // std::set contract. |
| 51 std::string ids_string = " "; | 48 std::string ids_string = " "; |
| 52 { | 49 { |
| 53 base::AutoLock scoped_lock(lock_); | 50 base::AutoLock scoped_lock(lock_); |
| 54 for (const VariationIDEntry& entry : GetAllVariationIds()) { | 51 for (VariationID id : GetAllVariationIds()) { |
| 55 if (entry.second == GOOGLE_WEB_PROPERTIES) { | 52 ids_string.append(base::IntToString(id)); |
| 56 ids_string.append(base::IntToString(entry.first)); | 53 ids_string.push_back(' '); |
| 57 ids_string.push_back(' '); | |
| 58 } | |
| 59 } | 54 } |
| 60 } | 55 } |
| 61 return ids_string; | 56 return ids_string; |
| 62 } | 57 } |
| 63 | 58 |
| 64 bool VariationsHttpHeaderProvider::ForceVariationIds( | 59 bool VariationsHttpHeaderProvider::ForceVariationIds( |
| 65 const std::string& command_line_variation_ids, | 60 const std::string& command_line_variation_ids, |
| 66 std::vector<std::string>* variation_ids) { | 61 std::vector<std::string>* variation_ids) { |
| 67 if (!command_line_variation_ids.empty()) { | 62 if (!command_line_variation_ids.empty()) { |
| 68 // Combine |variation_ids| with |command_line_variation_ids|. | 63 // Combine |variation_ids| with |command_line_variation_ids|. |
| 69 std::vector<std::string> variation_ids_flags = | 64 std::vector<std::string> variation_ids_flags = |
| 70 base::SplitString(command_line_variation_ids, ",", | 65 base::SplitString(command_line_variation_ids, ",", |
| 71 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 66 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| 72 variation_ids->insert(variation_ids->end(), variation_ids_flags.begin(), | 67 variation_ids->insert(variation_ids->end(), variation_ids_flags.begin(), |
| 73 variation_ids_flags.end()); | 68 variation_ids_flags.end()); |
| 74 } | 69 } |
| 75 | 70 |
| 76 if (!variation_ids->empty()) { | 71 if (!variation_ids->empty()) { |
| 77 // Create default variation ids which will always be included in the | 72 // Create default variation ids which will always be included in the |
| 78 // X-Client-Data request header. | 73 // X-Client-Data request header. |
| 79 return SetDefaultVariationIds(*variation_ids); | 74 return SetDefaultVariationIds(*variation_ids); |
| 80 } | 75 } |
| 81 return true; | 76 return true; |
| 82 } | 77 } |
| 83 | 78 |
| 79 |
| 84 bool VariationsHttpHeaderProvider::SetDefaultVariationIds( | 80 bool VariationsHttpHeaderProvider::SetDefaultVariationIds( |
| 85 const std::vector<std::string>& variation_ids) { | 81 const std::vector<std::string>& variation_ids) { |
| 86 default_variation_ids_set_.clear(); | 82 default_variation_ids_set_.clear(); |
| 83 default_trigger_id_set_.clear(); |
| 87 for (const std::string& entry : variation_ids) { | 84 for (const std::string& entry : variation_ids) { |
| 88 if (entry.empty()) { | 85 if (entry.empty()) { |
| 89 default_variation_ids_set_.clear(); | 86 default_variation_ids_set_.clear(); |
| 87 default_trigger_id_set_.clear(); |
| 90 return false; | 88 return false; |
| 91 } | 89 } |
| 92 bool trigger_id = | 90 bool trigger_id = |
| 93 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE); | 91 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE); |
| 94 // Remove the "t" prefix if it's there. | 92 // Remove the "t" prefix if it's there. |
| 95 std::string trimmed_entry = trigger_id ? entry.substr(1) : entry; | 93 std::string trimmed_entry = trigger_id ? entry.substr(1) : entry; |
| 96 | 94 |
| 97 int variation_id = 0; | 95 int variation_id = 0; |
| 98 if (!base::StringToInt(trimmed_entry, &variation_id)) { | 96 if (!base::StringToInt(trimmed_entry, &variation_id)) { |
| 99 default_variation_ids_set_.clear(); | 97 default_variation_ids_set_.clear(); |
| 98 default_trigger_id_set_.clear(); |
| 100 return false; | 99 return false; |
| 101 } | 100 } |
| 102 default_variation_ids_set_.insert(VariationIDEntry( | 101 if (trigger_id) |
| 103 variation_id, | 102 default_trigger_id_set_.insert(variation_id); |
| 104 trigger_id ? GOOGLE_WEB_PROPERTIES_TRIGGER : GOOGLE_WEB_PROPERTIES)); | 103 else |
| 104 default_variation_ids_set_.insert(variation_id); |
| 105 } | 105 } |
| 106 return true; | 106 return true; |
| 107 } | 107 } |
| 108 | 108 |
| 109 void VariationsHttpHeaderProvider::ResetForTesting() { | 109 void VariationsHttpHeaderProvider::ResetForTesting() { |
| 110 base::AutoLock scoped_lock(lock_); | 110 base::AutoLock scoped_lock(lock_); |
| 111 | 111 |
| 112 // Stop observing field trials so that it can be restarted when this is | 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. | 113 // re-inited. Note: This is a no-op if this is not currently observing. |
| 114 base::FieldTrialList::RemoveObserver(this); | 114 base::FieldTrialList::RemoveObserver(this); |
| 115 variation_ids_cache_initialized_ = false; | 115 variation_ids_cache_initialized_ = false; |
| 116 } | 116 } |
| 117 | 117 |
| 118 VariationsHttpHeaderProvider::VariationsHttpHeaderProvider() | 118 VariationsHttpHeaderProvider::VariationsHttpHeaderProvider() |
| 119 : variation_ids_cache_initialized_(false) {} | 119 : variation_ids_cache_initialized_(false) {} |
| 120 | 120 |
| 121 VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {} | 121 VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {} |
| 122 | 122 |
| 123 void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized( | 123 void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized( |
| 124 const std::string& trial_name, | 124 const std::string& trial_name, |
| 125 const std::string& group_name) { | 125 const std::string& group_name) { |
| 126 VariationID new_id = |
| 127 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name); |
| 128 VariationID new_trigger_id = GetGoogleVariationID( |
| 129 GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name); |
| 130 if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID) |
| 131 return; |
| 132 |
| 126 base::AutoLock scoped_lock(lock_); | 133 base::AutoLock scoped_lock(lock_); |
| 127 const size_t old_size = variation_ids_set_.size(); | 134 if (new_id != EMPTY_ID) |
| 128 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES); | 135 variation_ids_set_.insert(new_id); |
| 129 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_SIGNED_IN); | 136 if (new_trigger_id != EMPTY_ID) |
| 130 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_TRIGGER); | 137 variation_trigger_ids_set_.insert(new_trigger_id); |
| 131 if (variation_ids_set_.size() != old_size) | 138 |
| 132 UpdateVariationIDsHeaderValue(); | 139 UpdateVariationIDsHeaderValue(); |
| 133 } | 140 } |
| 134 | 141 |
| 135 void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged( | 142 void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged( |
| 136 const std::vector<SyntheticTrialGroup>& groups) { | 143 const std::vector<SyntheticTrialGroup>& groups) { |
| 137 base::AutoLock scoped_lock(lock_); | 144 base::AutoLock scoped_lock(lock_); |
| 138 | 145 |
| 139 synthetic_variation_ids_set_.clear(); | 146 synthetic_variation_ids_set_.clear(); |
| 140 for (const SyntheticTrialGroup& group : groups) { | 147 for (const SyntheticTrialGroup& group : groups) { |
| 141 VariationID id = | 148 const VariationID id = |
| 142 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id); | 149 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id); |
| 143 if (id != EMPTY_ID) { | 150 if (id != EMPTY_ID) |
| 144 synthetic_variation_ids_set_.insert( | 151 synthetic_variation_ids_set_.insert(id); |
| 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 } | |
| 153 } | 152 } |
| 154 UpdateVariationIDsHeaderValue(); | 153 UpdateVariationIDsHeaderValue(); |
| 155 } | 154 } |
| 156 | 155 |
| 157 void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() { | 156 void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() { |
| 158 base::AutoLock scoped_lock(lock_); | 157 base::AutoLock scoped_lock(lock_); |
| 159 if (variation_ids_cache_initialized_) | 158 if (variation_ids_cache_initialized_) |
| 160 return; | 159 return; |
| 161 | 160 |
| 162 // Register for additional cache updates. This is done first to avoid a race | 161 // Register for additional cache updates. This is done first to avoid a race |
| 163 // that could cause registered FieldTrials to be missed. | 162 // that could cause registered FieldTrials to be missed. |
| 164 DCHECK(base::ThreadTaskRunnerHandle::IsSet()); | 163 DCHECK(base::ThreadTaskRunnerHandle::IsSet()); |
| 165 base::FieldTrialList::AddObserver(this); | 164 base::FieldTrialList::AddObserver(this); |
| 166 | 165 |
| 167 base::TimeTicks before_time = base::TimeTicks::Now(); | 166 base::TimeTicks before_time = base::TimeTicks::Now(); |
| 168 | 167 |
| 169 base::FieldTrial::ActiveGroups initial_groups; | 168 base::FieldTrial::ActiveGroups initial_groups; |
| 170 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups); | 169 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups); |
| 171 | 170 |
| 172 for (const auto& entry : initial_groups) { | 171 for (const auto& entry : initial_groups) { |
| 173 CacheVariationsId(entry.trial_name, entry.group_name, | 172 const VariationID id = |
| 174 GOOGLE_WEB_PROPERTIES); | 173 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, entry.trial_name, |
| 175 CacheVariationsId(entry.trial_name, entry.group_name, | 174 entry.group_name); |
| 176 GOOGLE_WEB_PROPERTIES_SIGNED_IN); | 175 if (id != EMPTY_ID) |
| 177 CacheVariationsId(entry.trial_name, entry.group_name, | 176 variation_ids_set_.insert(id); |
| 178 GOOGLE_WEB_PROPERTIES_TRIGGER); | 177 |
| 178 const VariationID trigger_id = |
| 179 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER, entry.trial_name, |
| 180 entry.group_name); |
| 181 |
| 182 if (trigger_id != EMPTY_ID) |
| 183 variation_trigger_ids_set_.insert(trigger_id); |
| 179 } | 184 } |
| 180 UpdateVariationIDsHeaderValue(); | 185 UpdateVariationIDsHeaderValue(); |
| 181 | 186 |
| 182 UMA_HISTOGRAM_CUSTOM_COUNTS( | 187 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 183 "Variations.HeaderConstructionTime", | 188 "Variations.HeaderConstructionTime", |
| 184 (base::TimeTicks::Now() - before_time).InMicroseconds(), 1, | 189 (base::TimeTicks::Now() - before_time).InMicroseconds(), 1, |
| 185 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50); | 190 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50); |
| 186 | 191 |
| 187 variation_ids_cache_initialized_ = true; | 192 variation_ids_cache_initialized_ = true; |
| 188 } | 193 } |
| 189 | 194 |
| 190 void 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 | |
| 199 void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() { | 195 void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() { |
| 200 lock_.AssertAcquired(); | 196 lock_.AssertAcquired(); |
| 201 | 197 |
| 202 // The header value is a serialized protobuffer of Variation IDs which is | 198 // The header value is a serialized protobuffer of Variation IDs which is |
| 203 // base64 encoded before transmitting as a string. | 199 // base64 encoded before transmitting as a string. |
| 204 cached_variation_ids_header_.clear(); | 200 variation_ids_header_.clear(); |
| 205 cached_variation_ids_header_signed_in_.clear(); | |
| 206 | 201 |
| 207 // If successful, swap the header value with the new one. | 202 if (variation_ids_set_.empty() && default_variation_ids_set_.empty() && |
| 208 // Note that the list of IDs and the header could be temporarily out of sync | 203 variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty() && |
| 209 // if IDs are added as the header is recreated. The receiving servers are OK | 204 synthetic_variation_ids_set_.empty()) { |
| 210 // with such discrepancies. | 205 return; |
| 211 cached_variation_ids_header_ = GenerateBase64EncodedProto(false); | |
| 212 cached_variation_ids_header_signed_in_ = GenerateBase64EncodedProto(true); | |
| 213 } | |
| 214 | |
| 215 std::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; | |
| 232 case CHROME_SYNC_SERVICE: | |
| 233 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 } | |
| 238 } | 206 } |
| 239 | 207 |
| 240 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 | |
| 246 // This is the bottleneck for the creation of the header, so validate the size | 208 // 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 | 209 // 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. | 210 // returns too many IDs and DOSs receiving servers with large requests. |
| 211 const size_t total_id_count = |
| 212 variation_ids_set_.size() + variation_trigger_ids_set_.size(); |
| 249 DCHECK_LE(total_id_count, 10U); | 213 DCHECK_LE(total_id_count, 10U); |
| 250 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount", | 214 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount", |
| 251 total_id_count); | 215 total_id_count); |
| 252 if (total_id_count > 20) | 216 if (total_id_count > 20) |
| 253 return std::string(); | 217 return; |
| 218 |
| 219 std::set<VariationID> all_variation_ids_set = GetAllVariationIds(); |
| 220 std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_; |
| 221 for (VariationID id : variation_trigger_ids_set_) |
| 222 all_trigger_ids_set.insert(id); |
| 223 |
| 224 ClientVariations proto; |
| 225 for (VariationID id : all_variation_ids_set) |
| 226 proto.add_variation_id(id); |
| 227 for (VariationID id : all_trigger_ids_set) |
| 228 proto.add_trigger_variation_id(id); |
| 254 | 229 |
| 255 std::string serialized; | 230 std::string serialized; |
| 256 proto.SerializeToString(&serialized); | 231 proto.SerializeToString(&serialized); |
| 257 | 232 |
| 258 std::string hashed; | 233 std::string hashed; |
| 259 base::Base64Encode(serialized, &hashed); | 234 base::Base64Encode(serialized, &hashed); |
| 260 return hashed; | 235 // If successful, swap the header value with the new one. |
| 236 // Note that the list of IDs and the header could be temporarily out of sync |
| 237 // if IDs are added as the header is recreated. The receiving servers are OK |
| 238 // with such discrepancies. |
| 239 variation_ids_header_ = hashed; |
| 261 } | 240 } |
| 262 | 241 |
| 263 std::set<VariationsHttpHeaderProvider::VariationIDEntry> | 242 std::set<VariationID> VariationsHttpHeaderProvider::GetAllVariationIds() { |
| 264 VariationsHttpHeaderProvider::GetAllVariationIds() { | |
| 265 lock_.AssertAcquired(); | 243 lock_.AssertAcquired(); |
| 266 | 244 |
| 267 std::set<VariationIDEntry> all_variation_ids_set = default_variation_ids_set_; | 245 std::set<VariationID> all_variation_ids_set = default_variation_ids_set_; |
| 268 for (const VariationIDEntry& entry : variation_ids_set_) { | 246 for (VariationID id : variation_ids_set_) |
| 269 all_variation_ids_set.insert(entry); | 247 all_variation_ids_set.insert(id); |
| 270 } | 248 for (VariationID id : synthetic_variation_ids_set_) |
| 271 for (const VariationIDEntry& entry : synthetic_variation_ids_set_) { | 249 all_variation_ids_set.insert(id); |
| 272 all_variation_ids_set.insert(entry); | |
| 273 } | |
| 274 return all_variation_ids_set; | 250 return all_variation_ids_set; |
| 275 } | 251 } |
| 276 | 252 |
| 277 } // namespace variations | 253 } // namespace variations |
| OLD | NEW |