Commit 9ed7b561 authored by asvitkine's avatar asvitkine Committed by Commit bot

Restrict transmission of external exp ids to signed in users.

Since external experiment ids are not based on Chrome's low
entropy source, they do not have the same guarantees about
not identifying a user as Chrome's variations. As such, we
should only transmit them for signed in users, whose identity
is already known by Google so there's no risk of identifying
them through these headers.

Note: The signed-in state checking in this CL is only done for
web content area requests and not other internal requests,
like to the suggestion service, where it treats the state as
"not signed in". This is fine to do because variations service
ids are still sent, which is what the other call sites are
interested in.

BUG=672532
TBR=mpearson@chromium.org,mattm@chromium.org,donnd@chromium.org,afakhry@chromium.org

Review-Url: https://codereview.chromium.org/2558913003
Cr-Commit-Position: refs/heads/master@{#437959}
parent 54585e4c
......@@ -136,10 +136,13 @@ void ContextualSearchDelegate::ContinueSearchTermResolutionRequest() {
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
// Note: It's fine to pass in |is_signed_in| false, which does not affect
// transmission of experiment ids coming from the variations server.
bool is_signed_in = false;
variations::AppendVariationHeaders(
search_term_fetcher_->GetOriginalURL(),
false, // Impossible to be incognito at this point.
false, &headers);
false, is_signed_in, &headers);
search_term_fetcher_->SetExtraRequestHeaders(headers.ToString());
SetDiscourseContextAndAddToHeader(*context_);
......
......@@ -168,8 +168,11 @@ static void RegisterExternalExperiment(
active_group.name = metrics::HashName(trial_name_utf8);
for (int experiment_id : experiment_ids) {
active_group.group = metrics::HashName(base::IntToString(experiment_id));
// Since external experiments are not based on Chrome's low entropy source,
// they are only sent to Google web properties for signed in users to make
// sure that this couldn't be used to identify a user that's not signed in.
variations::AssociateGoogleVariationIDForceHashes(
variations::GOOGLE_WEB_PROPERTIES, active_group,
variations::GOOGLE_WEB_PROPERTIES_SIGNED_IN, active_group,
static_cast<variations::VariationID>(experiment_id));
group_name_hashes.push_back(active_group.group);
}
......
......@@ -485,8 +485,11 @@ void ChromeResourceDispatcherHostDelegate::RequestBeginning(
net::HttpRequestHeaders headers;
headers.CopyFrom(request->extra_request_headers());
bool is_off_the_record = io_data->IsOffTheRecord();
bool is_signed_in =
!is_off_the_record &&
!io_data->google_services_account_id()->GetValue().empty();
variations::AppendVariationHeaders(
request->url(), is_off_the_record,
request->url(), is_off_the_record, is_signed_in,
!is_off_the_record && io_data->GetMetricsEnabledStateOnIOThread(),
&headers);
request->SetExtraRequestHeaders(headers);
......
......@@ -632,10 +632,13 @@ class SRTFetcher : public net::URLFetcherDelegate {
ProfileIOData* io_data = ProfileIOData::FromResourceContext(
profile_->GetResourceContext());
net::HttpRequestHeaders headers;
// Note: It's fine to pass in |is_signed_in| false, which does not affect
// transmission of experiment ids coming from the variations server.
bool is_signed_in = false;
variations::AppendVariationHeaders(
url_fetcher_->GetOriginalURL(), io_data->IsOffTheRecord(),
ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(),
&headers);
is_signed_in, &headers);
url_fetcher_->SetExtraRequestHeaders(headers.ToString());
url_fetcher_->Start();
}
......
......@@ -247,8 +247,12 @@ bool AutofillDownloadManager::StartRequest(
net::LOAD_DO_NOT_SEND_COOKIES);
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
variations::AppendVariationHeaders(
fetcher->GetOriginalURL(), driver_->IsOffTheRecord(), false, &headers);
// Note: It's fine to pass in |is_signed_in| false, which does not affect
// transmission of experiment ids coming from the variations server.
bool is_signed_in = false;
variations::AppendVariationHeaders(fetcher->GetOriginalURL(),
driver_->IsOffTheRecord(), false,
is_signed_in, &headers);
fetcher->SetExtraRequestHeaders(headers.ToString());
fetcher->Start();
......
......@@ -60,8 +60,12 @@ void FeedbackUploaderChrome::DispatchReport(const std::string& data) {
fetcher, data_use_measurement::DataUseUserData::FEEDBACK_UPLOADER);
// Tell feedback server about the variation state of this install.
net::HttpRequestHeaders headers;
variations::AppendVariationHeaders(
fetcher->GetOriginalURL(), context_->IsOffTheRecord(), false, &headers);
// Note: It's fine to pass in |is_signed_in| false, which does not affect
// transmission of experiment ids coming from the variations server.
bool is_signed_in = false;
variations::AppendVariationHeaders(fetcher->GetOriginalURL(),
context_->IsOffTheRecord(), false,
is_signed_in, &headers);
fetcher->SetExtraRequestHeaders(headers.ToString());
fetcher->SetUploadData(kProtoBufMimeType, data);
......
......@@ -691,10 +691,13 @@ std::string NTPSnippetsFetcher::RequestBuilder::BuildHeaders() const {
headers.SetHeader("Authorization", auth_header_);
}
// Add X-Client-Data header with experiment IDs from field trials.
// Note: It's fine to pass in |is_signed_in| false, which does not affect
// transmission of experiment ids coming from the variations server.
bool is_signed_in = false;
variations::AppendVariationHeaders(url_,
false, // incognito
false, // uma_enabled
&headers);
is_signed_in, &headers);
return headers.ToString();
}
......
......@@ -890,8 +890,12 @@ std::unique_ptr<net::URLFetcher> SearchProvider::CreateSuggestFetcher(
fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
variations::AppendVariationHeaders(
fetcher->GetOriginalURL(), client()->IsOffTheRecord(), false, &headers);
// Note: It's fine to pass in |is_signed_in| false, which does not affect
// transmission of experiment ids coming from the variations server.
bool is_signed_in = false;
variations::AppendVariationHeaders(fetcher->GetOriginalURL(),
client()->IsOffTheRecord(), false,
is_signed_in, &headers);
fetcher->SetExtraRequestHeaders(headers.ToString());
fetcher->Start();
return fetcher;
......
......@@ -333,9 +333,12 @@ void ZeroSuggestProvider::Run(const GURL& suggest_url) {
fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
// Note: It's fine to pass in |is_signed_in| false, which does not affect
// transmission of experiment ids coming from the variations server.
bool is_signed_in = false;
variations::AppendVariationHeaders(fetcher_->GetOriginalURL(),
client()->IsOffTheRecord(), false,
&headers);
is_signed_in, &headers);
fetcher_->SetExtraRequestHeaders(headers.ToString());
fetcher_->Start();
LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_SENT);
......
......@@ -420,8 +420,11 @@ std::unique_ptr<net::URLFetcher> SuggestionsService::CreateSuggestionsRequest(
request->SetRequestContext(url_request_context_);
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
// Note: It's fine to pass in |is_signed_in| false, which does not affect
// transmission of experiment ids coming from the variations server.
bool is_signed_in = false;
variations::AppendVariationHeaders(request->GetOriginalURL(), false, false,
&headers);
is_signed_in, &headers);
request->SetExtraRequestHeaders(headers.ToString());
if (!access_token.empty()) {
request->AddExtraRequestHeader(
......
......@@ -44,6 +44,7 @@ const char kClientData[] = "X-Client-Data";
void AppendVariationHeaders(const GURL& url,
bool incognito,
bool uma_enabled,
bool is_signed_in,
net::HttpRequestHeaders* headers) {
// Note the criteria for attaching client experiment headers:
// 1. We only transmit to Google owned domains which can evaluate experiments.
......@@ -62,7 +63,8 @@ void AppendVariationHeaders(const GURL& url,
headers->SetHeaderIfMissing(kChromeUMAEnabled, "1");
const std::string variation_ids_header =
VariationsHttpHeaderProvider::GetInstance()->GetClientDataHeader();
VariationsHttpHeaderProvider::GetInstance()->GetClientDataHeader(
is_signed_in);
if (!variation_ids_header.empty()) {
// Note that prior to M33 this header was named X-Chrome-Variations.
headers->SetHeaderIfMissing(kClientData, variation_ids_header);
......
......@@ -17,12 +17,16 @@ class GURL;
namespace variations {
// Adds Chrome experiment and metrics state as custom headers to |headers|.
// Some headers may not be set given the |incognito| mode or whether
// the user has |uma_enabled|. Also, we never transmit headers to non-Google
// sites, which is checked based on the destination |url|.
// The content of the headers will depend on |incognito|, |uma_enabled| and
// |is_signed_in| parameters. It is fine to pass false for |is_signed_in| if the
// state is not known to the caller. This will prevent addition of ids of type
// GOOGLE_WEB_PROPERTIES_SIGNED_IN, which is not the case for any ids that come
// from the variations server. These headers are never transmitted to non-Google
// web sites, which is checked based on the destination |url|.
void AppendVariationHeaders(const GURL& url,
bool incognito,
bool uma_enabled,
bool is_signed_in,
net::HttpRequestHeaders* headers);
// Returns the HTTP header names which are added by AppendVariationHeaders().
......
......@@ -39,13 +39,21 @@ class GroupMapAccessor {
const VariationID id,
const bool force) {
#if !defined(NDEBUG)
DCHECK_EQ(3, ID_COLLECTION_COUNT);
// Ensure that at most one of the trigger/non-trigger web property IDs are
// set.
if (key == GOOGLE_WEB_PROPERTIES || key == GOOGLE_WEB_PROPERTIES_TRIGGER) {
IDCollectionKey other_key = key == GOOGLE_WEB_PROPERTIES ?
GOOGLE_WEB_PROPERTIES_TRIGGER : GOOGLE_WEB_PROPERTIES;
DCHECK_EQ(EMPTY_ID, GetID(other_key, group_identifier));
DCHECK_EQ(4, ID_COLLECTION_COUNT);
// Ensure that at most one of the trigger/non-trigger/signed-in web property
// IDs are set.
if (key == GOOGLE_WEB_PROPERTIES || key == GOOGLE_WEB_PROPERTIES_TRIGGER ||
key == GOOGLE_WEB_PROPERTIES_SIGNED_IN) {
if (key != GOOGLE_WEB_PROPERTIES)
DCHECK_EQ(EMPTY_ID, GetID(GOOGLE_WEB_PROPERTIES, group_identifier));
if (key != GOOGLE_WEB_PROPERTIES_TRIGGER) {
DCHECK_EQ(EMPTY_ID,
GetID(GOOGLE_WEB_PROPERTIES_TRIGGER, group_identifier));
}
if (key != GOOGLE_WEB_PROPERTIES_SIGNED_IN) {
DCHECK_EQ(EMPTY_ID,
GetID(GOOGLE_WEB_PROPERTIES_SIGNED_IN, group_identifier));
}
}
// Validate that all collections with this |group_identifier| have the same
......
......@@ -58,6 +58,9 @@ enum IDCollectionKey {
// This collection is used by Google web properties, transmitted through the
// X-Client-Data header.
GOOGLE_WEB_PROPERTIES,
// This collection is used by Google web properties for signed in users only,
// transmitted through the X-Client-Data header.
GOOGLE_WEB_PROPERTIES_SIGNED_IN,
// This collection is used by Google web properties for IDs that trigger
// server side experimental behavior, transmitted through the
// X-Client-Data header.
......
......@@ -26,7 +26,8 @@ VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
return base::Singleton<VariationsHttpHeaderProvider>::get();
}
std::string VariationsHttpHeaderProvider::GetClientDataHeader() {
std::string VariationsHttpHeaderProvider::GetClientDataHeader(
bool is_signed_in) {
// Lazily initialize the header, if not already done, before attempting to
// transmit it.
InitVariationIDsCacheIfNeeded();
......@@ -34,7 +35,9 @@ std::string VariationsHttpHeaderProvider::GetClientDataHeader() {
std::string variation_ids_header_copy;
{
base::AutoLock scoped_lock(lock_);
variation_ids_header_copy = variation_ids_header_;
variation_ids_header_copy = is_signed_in
? cached_variation_ids_header_signed_in_
: cached_variation_ids_header_;
}
return variation_ids_header_copy;
}
......@@ -48,9 +51,11 @@ std::string VariationsHttpHeaderProvider::GetVariationsString() {
std::string ids_string = " ";
{
base::AutoLock scoped_lock(lock_);
for (VariationID id : GetAllVariationIds()) {
ids_string.append(base::IntToString(id));
ids_string.push_back(' ');
for (const VariationIDEntry& entry : GetAllVariationIds()) {
if (entry.second == GOOGLE_WEB_PROPERTIES) {
ids_string.append(base::IntToString(entry.first));
ids_string.push_back(' ');
}
}
}
return ids_string;
......@@ -76,15 +81,12 @@ bool VariationsHttpHeaderProvider::ForceVariationIds(
return true;
}
bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
const std::vector<std::string>& variation_ids) {
default_variation_ids_set_.clear();
default_trigger_id_set_.clear();
for (const std::string& entry : variation_ids) {
if (entry.empty()) {
default_variation_ids_set_.clear();
default_trigger_id_set_.clear();
return false;
}
bool trigger_id =
......@@ -95,13 +97,11 @@ bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
int variation_id = 0;
if (!base::StringToInt(trimmed_entry, &variation_id)) {
default_variation_ids_set_.clear();
default_trigger_id_set_.clear();
return false;
}
if (trigger_id)
default_trigger_id_set_.insert(variation_id);
else
default_variation_ids_set_.insert(variation_id);
default_variation_ids_set_.insert(VariationIDEntry(
variation_id,
trigger_id ? GOOGLE_WEB_PROPERTIES_TRIGGER : GOOGLE_WEB_PROPERTIES));
}
return true;
}
......@@ -123,20 +123,13 @@ VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {}
void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
const std::string& trial_name,
const std::string& group_name) {
VariationID new_id =
GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
VariationID new_trigger_id = GetGoogleVariationID(
GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name);
if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID)
return;
base::AutoLock scoped_lock(lock_);
if (new_id != EMPTY_ID)
variation_ids_set_.insert(new_id);
if (new_trigger_id != EMPTY_ID)
variation_trigger_ids_set_.insert(new_trigger_id);
UpdateVariationIDsHeaderValue();
const size_t old_size = variation_ids_set_.size();
CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES);
CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_SIGNED_IN);
CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_TRIGGER);
if (variation_ids_set_.size() != old_size)
UpdateVariationIDsHeaderValue();
}
void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
......@@ -145,10 +138,18 @@ void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
synthetic_variation_ids_set_.clear();
for (const SyntheticTrialGroup& group : groups) {
const VariationID id =
VariationID id =
GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id);
if (id != EMPTY_ID)
synthetic_variation_ids_set_.insert(id);
if (id != EMPTY_ID) {
synthetic_variation_ids_set_.insert(
VariationIDEntry(id, GOOGLE_WEB_PROPERTIES));
}
id = GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES_SIGNED_IN,
group.id);
if (id != EMPTY_ID) {
synthetic_variation_ids_set_.insert(
VariationIDEntry(id, GOOGLE_WEB_PROPERTIES_SIGNED_IN));
}
}
UpdateVariationIDsHeaderValue();
}
......@@ -169,18 +170,12 @@ void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
for (const auto& entry : initial_groups) {
const VariationID id =
GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, entry.trial_name,
entry.group_name);
if (id != EMPTY_ID)
variation_ids_set_.insert(id);
const VariationID trigger_id =
GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER, entry.trial_name,
entry.group_name);
if (trigger_id != EMPTY_ID)
variation_trigger_ids_set_.insert(trigger_id);
CacheVariationsId(entry.trial_name, entry.group_name,
GOOGLE_WEB_PROPERTIES);
CacheVariationsId(entry.trial_name, entry.group_name,
GOOGLE_WEB_PROPERTIES_SIGNED_IN);
CacheVariationsId(entry.trial_name, entry.group_name,
GOOGLE_WEB_PROPERTIES_TRIGGER);
}
UpdateVariationIDsHeaderValue();
......@@ -192,61 +187,90 @@ void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
variation_ids_cache_initialized_ = true;
}
void VariationsHttpHeaderProvider::CacheVariationsId(
const std::string& trial_name,
const std::string& group_name,
IDCollectionKey key) {
const VariationID id = GetGoogleVariationID(key, trial_name, group_name);
if (id != EMPTY_ID)
variation_ids_set_.insert(VariationIDEntry(id, key));
}
void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
lock_.AssertAcquired();
// The header value is a serialized protobuffer of Variation IDs which is
// base64 encoded before transmitting as a string.
variation_ids_header_.clear();
cached_variation_ids_header_.clear();
cached_variation_ids_header_signed_in_.clear();
if (variation_ids_set_.empty() && default_variation_ids_set_.empty() &&
variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty() &&
synthetic_variation_ids_set_.empty()) {
return;
// If successful, swap the header value with the new one.
// Note that the list of IDs and the header could be temporarily out of sync
// if IDs are added as the header is recreated. The receiving servers are OK
// with such discrepancies.
cached_variation_ids_header_ = GenerateBase64EncodedProto(false);
cached_variation_ids_header_signed_in_ = GenerateBase64EncodedProto(true);
}
std::string VariationsHttpHeaderProvider::GenerateBase64EncodedProto(
bool is_signed_in) {
std::set<VariationIDEntry> all_variation_ids_set = GetAllVariationIds();
ClientVariations proto;
for (const VariationIDEntry& entry : all_variation_ids_set) {
switch (entry.second) {
case GOOGLE_WEB_PROPERTIES_SIGNED_IN:
if (is_signed_in)
proto.add_variation_id(entry.first);
break;
case GOOGLE_WEB_PROPERTIES:
proto.add_variation_id(entry.first);
break;
case GOOGLE_WEB_PROPERTIES_TRIGGER:
proto.add_trigger_variation_id(entry.first);
break;
case CHROME_SYNC_SERVICE:
case ID_COLLECTION_COUNT:
// These cases included to get full enum coverage for switch, so that
// new enums introduce compiler warnings. Nothing to do for these.
break;
}
}
const size_t total_id_count =
proto.variation_id_size() + proto.trigger_variation_id_size();
if (total_id_count == 0)
return std::string();
// This is the bottleneck for the creation of the header, so validate the size
// here. Force a hard maximum on the ID count in case the Variations server
// returns too many IDs and DOSs receiving servers with large requests.
const size_t total_id_count =
variation_ids_set_.size() + variation_trigger_ids_set_.size();
DCHECK_LE(total_id_count, 10U);
UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
total_id_count);
if (total_id_count > 20)
return;
std::set<VariationID> all_variation_ids_set = GetAllVariationIds();
std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_;
for (VariationID id : variation_trigger_ids_set_)
all_trigger_ids_set.insert(id);
ClientVariations proto;
for (VariationID id : all_variation_ids_set)
proto.add_variation_id(id);
for (VariationID id : all_trigger_ids_set)
proto.add_trigger_variation_id(id);
return std::string();
std::string serialized;
proto.SerializeToString(&serialized);
std::string hashed;
base::Base64Encode(serialized, &hashed);
// If successful, swap the header value with the new one.
// Note that the list of IDs and the header could be temporarily out of sync
// if IDs are added as the header is recreated. The receiving servers are OK
// with such discrepancies.
variation_ids_header_ = hashed;
return hashed;
}
std::set<VariationID> VariationsHttpHeaderProvider::GetAllVariationIds() {
std::set<VariationsHttpHeaderProvider::VariationIDEntry>
VariationsHttpHeaderProvider::GetAllVariationIds() {
lock_.AssertAcquired();
std::set<VariationID> all_variation_ids_set = default_variation_ids_set_;
for (VariationID id : variation_ids_set_)
all_variation_ids_set.insert(id);
for (VariationID id : synthetic_variation_ids_set_)
all_variation_ids_set.insert(id);
std::set<VariationIDEntry> all_variation_ids_set = default_variation_ids_set_;
for (const VariationIDEntry& entry : variation_ids_set_) {
all_variation_ids_set.insert(entry);
}
for (const VariationIDEntry& entry : synthetic_variation_ids_set_) {
all_variation_ids_set.insert(entry);
}
return all_variation_ids_set;
}
......
......@@ -7,6 +7,7 @@
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/gtest_prod_util.h"
......@@ -32,12 +33,15 @@ class VariationsHttpHeaderProvider : public base::FieldTrialList::Observer,
static VariationsHttpHeaderProvider* GetInstance();
// Returns the value of the client data header, computing and caching it if
// necessary.
std::string GetClientDataHeader();
// necessary. If |is_signed_in| is false, variation ids that should only be
// sent for signed in users (i.e. GOOGLE_WEB_PROPERTIES_SIGNED_IN entries)
// will not be included.
std::string GetClientDataHeader(bool is_signed_in);
// Returns a space-separated string containing the list of current active
// variations (as would be reported in the |variation_id| repeated field of
// the ClientVariations proto). The returned string is guaranteed to have a
// the ClientVariations proto). Does not include variation ids that should be
// sent for signed-in users only. The returned string is guaranteed to have a
// a leading and trailing space, e.g. " 123 234 345 ".
std::string GetVariationsString();
......@@ -61,6 +65,8 @@ class VariationsHttpHeaderProvider : public base::FieldTrialList::Observer,
private:
friend struct base::DefaultSingletonTraits<VariationsHttpHeaderProvider>;
typedef std::pair<VariationID, IDCollectionKey> VariationIDEntry;
FRIEND_TEST_ALL_PREFIXES(VariationsHttpHeaderProviderTest,
SetDefaultVariationIds_Valid);
FRIEND_TEST_ALL_PREFIXES(VariationsHttpHeaderProviderTest,
......@@ -88,35 +94,43 @@ class VariationsHttpHeaderProvider : public base::FieldTrialList::Observer,
// new variation IDs.
void InitVariationIDsCacheIfNeeded();
// Looks up the associated id for the given trial/group and adds an entry for
// it to |variation_ids_set_| if found.
void CacheVariationsId(const std::string& trial_name,
const std::string& group_name,
IDCollectionKey key);
// Takes whatever is currently in |variation_ids_set_| and recreates
// |variation_ids_header_| with it. Assumes the the |lock_| is currently
// held.
void UpdateVariationIDsHeaderValue();
// Generates a base64-encoded proto to be used as a header value for the given
// |is_signed_in| state.
std::string GenerateBase64EncodedProto(bool is_signed_in);
// Returns the currently active set of variation ids, which includes any
// default values, synthetic variations and actual field trial variations.
std::set<VariationID> GetAllVariationIds();
std::set<VariationIDEntry> GetAllVariationIds();
// Guards |variation_ids_cache_initialized_|, |variation_ids_set_| and
// |variation_ids_header_|.
// Guards access to variables below.
base::Lock lock_;
// Whether or not we've initialized the cache.
// Whether or not we've initialized the caches.
bool variation_ids_cache_initialized_;
// Keep a cache of variation IDs that are transmitted in headers to Google.
// This consists of a list of valid IDs, and the actual transmitted header.
std::set<VariationID> variation_ids_set_;
std::set<VariationID> variation_trigger_ids_set_;
std::set<VariationIDEntry> variation_ids_set_;
// Provides the google experiment ids forced from command line.
std::set<VariationID> default_variation_ids_set_;
std::set<VariationID> default_trigger_id_set_;
std::set<VariationIDEntry> default_variation_ids_set_;
// Variations ids from synthetic field trials.
std::set<VariationID> synthetic_variation_ids_set_;
std::set<VariationIDEntry> synthetic_variation_ids_set_;
std::string variation_ids_header_;
std::string cached_variation_ids_header_;
std::string cached_variation_ids_header_signed_in_;
DISALLOW_COPY_AND_ASSIGN(VariationsHttpHeaderProvider);
};
......
......@@ -67,7 +67,7 @@ TEST_F(VariationsHttpHeaderProviderTest, SetDefaultVariationIds_Valid) {
// Valid experiment ids.
EXPECT_TRUE(provider.SetDefaultVariationIds({"12", "456", "t789"}));
provider.InitVariationIDsCacheIfNeeded();
std::string variations = provider.GetClientDataHeader();
std::string variations = provider.GetClientDataHeader(false);
EXPECT_FALSE(variations.empty());
std::set<VariationID> variation_ids;
std::set<VariationID> trigger_ids;
......@@ -86,13 +86,13 @@ TEST_F(VariationsHttpHeaderProviderTest, SetDefaultVariationIds_Invalid) {
EXPECT_FALSE(provider.SetDefaultVariationIds(
std::vector<std::string>{"abcd12", "456"}));
provider.InitVariationIDsCacheIfNeeded();
EXPECT_TRUE(provider.GetClientDataHeader().empty());
EXPECT_TRUE(provider.GetClientDataHeader(false).empty());
// Invalid trigger experiment id
EXPECT_FALSE(provider.SetDefaultVariationIds(
std::vector<std::string>{"12", "tabc456"}));
provider.InitVariationIDsCacheIfNeeded();
EXPECT_TRUE(provider.GetClientDataHeader().empty());
EXPECT_TRUE(provider.GetClientDataHeader(false).empty());
}
TEST_F(VariationsHttpHeaderProviderTest, OnFieldTrialGroupFinalized) {
......@@ -104,25 +104,44 @@ TEST_F(VariationsHttpHeaderProviderTest, OnFieldTrialGroupFinalized) {
const std::string default_name = "default";
scoped_refptr<base::FieldTrial> trial_1(CreateTrialAndAssociateId(
"t1", default_name, GOOGLE_WEB_PROPERTIES, 123));
ASSERT_EQ(default_name, trial_1->group_name());
scoped_refptr<base::FieldTrial> trial_2(CreateTrialAndAssociateId(
"t2", default_name, GOOGLE_WEB_PROPERTIES_TRIGGER, 456));
ASSERT_EQ(default_name, trial_2->group_name());
scoped_refptr<base::FieldTrial> trial_3(CreateTrialAndAssociateId(
"t3", default_name, GOOGLE_WEB_PROPERTIES_SIGNED_IN, 789));
ASSERT_EQ(default_name, trial_3->group_name());
// Run the message loop to make sure OnFieldTrialGroupFinalized is called for
// the two field trials.
base::RunLoop().RunUntilIdle();
std::string variations = provider.GetClientDataHeader();
std::set<VariationID> variation_ids;
std::set<VariationID> trigger_ids;
ASSERT_TRUE(ExtractVariationIds(variations, &variation_ids, &trigger_ids));
EXPECT_TRUE(variation_ids.find(123) != variation_ids.end());
EXPECT_TRUE(trigger_ids.find(456) != trigger_ids.end());
// Get non-signed in ids.
{
std::string variations = provider.GetClientDataHeader(false);
std::set<VariationID> variation_ids;
std::set<VariationID> trigger_ids;
ASSERT_TRUE(ExtractVariationIds(variations, &variation_ids, &trigger_ids));
EXPECT_EQ(1U, variation_ids.size());
EXPECT_TRUE(variation_ids.find(123) != variation_ids.end());
EXPECT_EQ(1U, trigger_ids.size());
EXPECT_TRUE(trigger_ids.find(456) != trigger_ids.end());
}
// Now, get signed-in ids.
{
std::string variations = provider.GetClientDataHeader(true);
std::set<VariationID> variation_ids;
std::set<VariationID> trigger_ids;
ASSERT_TRUE(ExtractVariationIds(variations, &variation_ids, &trigger_ids));
EXPECT_EQ(2U, variation_ids.size());
EXPECT_TRUE(variation_ids.find(123) != variation_ids.end());
EXPECT_TRUE(variation_ids.find(789) != variation_ids.end());
EXPECT_EQ(1U, trigger_ids.size());
EXPECT_TRUE(trigger_ids.find(456) != trigger_ids.end());
}
}
TEST_F(VariationsHttpHeaderProviderTest, GetVariationsString) {
......@@ -131,6 +150,8 @@ TEST_F(VariationsHttpHeaderProviderTest, GetVariationsString) {
CreateTrialAndAssociateId("t1", "g1", GOOGLE_WEB_PROPERTIES, 123);
CreateTrialAndAssociateId("t2", "g2", GOOGLE_WEB_PROPERTIES, 124);
// SIGNED_IN ids shouldn't be included.
CreateTrialAndAssociateId("t3", "g3", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
VariationsHttpHeaderProvider provider;
std::vector<std::string> ids;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment