summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorveranika <veranika@chromium.org>2015-10-19 09:37:56 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-19 16:38:43 +0000
commitf6acefe76a605a6fc17714306919c56ab3b2e2af (patch)
tree07cbf238cf8e98a2822b955282b9c7d13e4ae956
parent91f64126aba750764c13d2278eda7147c5b98368 (diff)
downloadchromium_src-f6acefe76a605a6fc17714306919c56ab3b2e2af.zip
chromium_src-f6acefe76a605a6fc17714306919c56ab3b2e2af.tar.gz
chromium_src-f6acefe76a605a6fc17714306919c56ab3b2e2af.tar.bz2
Support gzip-compressed seed data from the variations server.
Firstly, the client asks for gzip-compressed data by adding "gzip" to A-IM HTTP request header. Secondly, if "gzip" is present in IM HTTP response header, the client uncompresses data. Gzip compression could be used in combination with delta compression. A-IM and IM headers are defined in RFC 3229: http://tools.ietf.org/html/rfc3229#page-32 This CL re-adds components/variations/service tests to components_unittests. BUG=543271 Review URL: https://codereview.chromium.org/1404583004 Cr-Commit-Position: refs/heads/master@{#354788}
-rw-r--r--components/BUILD.gn1
-rw-r--r--components/variations/service/variations_service.cc76
-rw-r--r--components/variations/service/variations_service.h10
-rw-r--r--components/variations/service/variations_service_unittest.cc112
-rw-r--r--components/variations/variations_seed_store.cc47
-rw-r--r--components/variations/variations_seed_store.h8
-rw-r--r--components/variations/variations_seed_store_unittest.cc44
-rw-r--r--tools/metrics/histograms/histograms.xml20
8 files changed, 280 insertions, 38 deletions
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 03c729c..c3ac7cd 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -397,6 +397,7 @@ test("components_unittests") {
"//components/upload_list:unit_tests",
"//components/user_prefs/tracked:unit_tests",
"//components/variations:unit_tests",
+ "//components/variations/service:unit_tests",
"//components/wallpaper:unit_tests",
"//components/web_resource:unit_tests",
"//components/webcrypto:unit_tests",
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc
index 2607ead..8d3ced5 100644
--- a/components/variations/service/variations_service.cc
+++ b/components/variations/service/variations_service.cc
@@ -10,6 +10,7 @@
#include "base/metrics/sparse_histogram.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
+#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h"
#include "base/timer/elapsed_timer.h"
@@ -214,6 +215,55 @@ std::string GetHeaderValue(const net::HttpResponseHeaders* headers,
return value;
}
+// Returns the list of values for |name| from |headers|. If the header in not
+// set, return an empty list.
+std::vector<std::string> GetHeaderValuesList(
+ const net::HttpResponseHeaders* headers,
+ const base::StringPiece& name) {
+ std::vector<std::string> values;
+ void* iter = NULL;
+ std::string value;
+ while (headers->EnumerateHeader(&iter, name, &value)) {
+ values.push_back(value);
+ }
+ return values;
+}
+
+// Looks for delta and gzip compression instance manipulation flags set by the
+// server in |headers|. Checks the order of flags and presence of unknown
+// instance manipulations. If successful, |is_delta_compressed| and
+// |is_gzip_compressed| contain compression flags and true is returned.
+bool GetInstanceManipulations(const net::HttpResponseHeaders* headers,
+ bool* is_delta_compressed,
+ bool* is_gzip_compressed) {
+ std::vector<std::string> ims = GetHeaderValuesList(headers, "IM");
+ const auto delta_im = std::find(ims.begin(), ims.end(), "x-bm");
+ const auto gzip_im = std::find(ims.begin(), ims.end(), "gzip");
+ *is_delta_compressed = delta_im != ims.end();
+ *is_gzip_compressed = gzip_im != ims.end();
+
+ // The IM field should not have anything but x-bm and gzip.
+ size_t im_count = (*is_delta_compressed ? 1 : 0) +
+ (*is_gzip_compressed ? 1 : 0);
+ if (im_count != ims.size()) {
+ DVLOG(1) << "Unrecognized instance manipulations in "
+ << base::JoinString(ims, ",")
+ << "; only x-bm and gzip are supported";
+ return false;
+ }
+
+ // The IM field defines order in which instance manipulations were applied.
+ // The client requests and supports gzip-compressed delta-compressed seeds,
+ // but not vice versa.
+ if (*is_delta_compressed && *is_gzip_compressed && delta_im > gzip_im) {
+ DVLOG(1) << "Unsupported instance manipulations order: "
+ << "requested x-bm,gzip but received gzip,x-bm";
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
VariationsService::VariationsService(
@@ -474,6 +524,7 @@ void VariationsService::DoActualFetch() {
net::LOAD_DO_NOT_SAVE_COOKIES);
pending_seed_request_->SetRequestContext(client_->GetURLRequestContext());
pending_seed_request_->SetMaxRetriesOn5xx(kMaxRetrySeedFetch);
+ bool enable_deltas = false;
if (!seed_store_.variations_serial_number().empty() &&
!disable_deltas_for_next_request_) {
// If the current seed includes a country code, deltas are not supported (as
@@ -484,12 +535,16 @@ void VariationsService::DoActualFetch() {
// that have an old seed with a country code becomes miniscule.
if (!seed_store_.seed_has_country_code()) {
// Tell the server that delta-compressed seeds are supported.
- pending_seed_request_->AddExtraRequestHeader("A-IM:x-bm");
+ enable_deltas = true;
}
// Get the seed only if its serial number doesn't match what we have.
pending_seed_request_->AddExtraRequestHeader(
"If-None-Match:" + seed_store_.variations_serial_number());
}
+ // Tell the server that delta-compressed and gzipped seeds are supported.
+ const char* supported_im = enable_deltas ? "A-IM:x-bm,gzip" : "A-IM:gzip";
+ pending_seed_request_->AddExtraRequestHeader(supported_im);
+
pending_seed_request_->Start();
const base::TimeTicks now = base::TimeTicks::Now();
@@ -510,13 +565,14 @@ bool VariationsService::StoreSeed(const std::string& seed_data,
const std::string& seed_signature,
const std::string& country_code,
const base::Time& date_fetched,
- bool is_delta_compressed) {
+ bool is_delta_compressed,
+ bool is_gzip_compressed) {
DCHECK(thread_checker_.CalledOnValidThread());
scoped_ptr<variations::VariationsSeed> seed(new variations::VariationsSeed);
if (!seed_store_.StoreSeedData(seed_data, seed_signature, country_code,
date_fetched, is_delta_compressed,
- seed.get())) {
+ is_gzip_compressed, seed.get())) {
return false;
}
RecordLastFetchTime();
@@ -628,11 +684,21 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK(success);
net::HttpResponseHeaders* headers = request->GetResponseHeaders();
+ bool is_delta_compressed;
+ bool is_gzip_compressed;
+ if (!GetInstanceManipulations(headers, &is_delta_compressed,
+ &is_gzip_compressed)) {
+ // The header does not specify supported instance manipulations, unable to
+ // process data. Details of errors were logged by GetInstanceManipulations.
+ seed_store_.ReportUnsupportedSeedFormatError();
+ return;
+ }
+
const std::string signature = GetHeaderValue(headers, "X-Seed-Signature");
const std::string country_code = GetHeaderValue(headers, "X-Country");
- const bool is_delta_compressed = (GetHeaderValue(headers, "IM") == "x-bm");
const bool store_success = StoreSeed(seed_data, signature, country_code,
- response_date, is_delta_compressed);
+ response_date, is_delta_compressed,
+ is_gzip_compressed);
if (!store_success && is_delta_compressed) {
disable_deltas_for_next_request_ = true;
request_scheduler_->ScheduleFetchShortly();
diff --git a/components/variations/service/variations_service.h b/components/variations/service/variations_service.h
index e9b786f..0f65da0 100644
--- a/components/variations/service/variations_service.h
+++ b/components/variations/service/variations_service.h
@@ -167,7 +167,8 @@ class VariationsService
const std::string& seed_signature,
const std::string& country_code,
const base::Time& date_fetched,
- bool is_delta_compressed);
+ bool is_delta_compressed,
+ bool is_gzip_compressed);
// Creates the VariationsService with the given |local_state| prefs service
// and |state_manager|. Does not take ownership of |state_manager|. Caller
@@ -180,11 +181,16 @@ class VariationsService
metrics::MetricsStateManager* state_manager,
const UIStringOverrider& ui_string_overrider);
+ // Sets the URL for querying the variations server. Used for testing.
+ void set_variations_server_url(const GURL& url) {
+ variations_server_url_ = url;
+ }
+
private:
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, Observer);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, SeedStoredWhenOKStatus);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, SeedNotStoredWhenNonOKStatus);
- FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, SeedDateUpdatedOn304Status);
+ FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, InstanceManipulations);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest,
LoadPermanentConsistencyCountry);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, CountryHeader);
diff --git a/components/variations/service/variations_service_unittest.cc b/components/variations/service/variations_service_unittest.cc
index 1b85ce7..fcc0ff5 100644
--- a/components/variations/service/variations_service_unittest.cc
+++ b/components/variations/service/variations_service_unittest.cc
@@ -83,9 +83,13 @@ class TestVariationsService : public VariationsService {
UIStringOverrider()),
intercepts_fetch_(true),
fetch_attempted_(false),
- seed_stored_(false) {
+ seed_stored_(false),
+ delta_compressed_seed_(false),
+ gzip_compressed_seed_(false) {
// Set this so StartRepeatedVariationsSeedFetch can be called in tests.
SetCreateTrialsFromSeedCalledForTesting(true);
+ set_variations_server_url(
+ GetVariationsServerURL(local_state, std::string()));
}
~TestVariationsService() override {}
@@ -97,6 +101,8 @@ class TestVariationsService : public VariationsService {
bool fetch_attempted() const { return fetch_attempted_; }
bool seed_stored() const { return seed_stored_; }
const std::string& stored_country() const { return stored_country_; }
+ bool delta_compressed_seed() const { return delta_compressed_seed_; }
+ bool gzip_compressed_seed() const { return gzip_compressed_seed_; }
void DoActualFetch() override {
if (intercepts_fetch_) {
@@ -111,10 +117,13 @@ class TestVariationsService : public VariationsService {
const std::string& seed_signature,
const std::string& country_code,
const base::Time& date_fetched,
- bool is_delta_compressed) override {
+ bool is_delta_compressed,
+ bool is_gzip_compressed) override {
seed_stored_ = true;
stored_seed_data_ = seed_data;
stored_country_ = country_code;
+ delta_compressed_seed_ = is_delta_compressed;
+ gzip_compressed_seed_ = is_gzip_compressed;
return true;
}
@@ -130,6 +139,8 @@ class TestVariationsService : public VariationsService {
bool seed_stored_;
std::string stored_seed_data_;
std::string stored_country_;
+ bool delta_compressed_seed_;
+ bool gzip_compressed_seed_;
DISALLOW_COPY_AND_ASSIGN(TestVariationsService);
};
@@ -201,18 +212,30 @@ std::string SerializeSeed(const variations::VariationsSeed& seed) {
}
// Simulates a variations service response by setting a date header and the
-// specified HTTP |response_code| on |fetcher|.
-scoped_refptr<net::HttpResponseHeaders> SimulateServerResponse(
+// specified HTTP |response_code| on |fetcher|. Sets additional header |header|
+// if it is not null.
+scoped_refptr<net::HttpResponseHeaders> SimulateServerResponseWithHeader(
int response_code,
- net::TestURLFetcher* fetcher) {
+ net::TestURLFetcher* fetcher,
+ const std::string* header) {
EXPECT_TRUE(fetcher);
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0"));
+ if (header)
+ headers->AddHeader(*header);
fetcher->set_response_headers(headers);
fetcher->set_response_code(response_code);
return headers;
}
+// Simulates a variations service response by setting a date header and the
+// specified HTTP |response_code| on |fetcher|.
+scoped_refptr<net::HttpResponseHeaders> SimulateServerResponse(
+ int response_code,
+ net::TestURLFetcher* fetcher) {
+ return SimulateServerResponseWithHeader(response_code, fetcher, nullptr);
+}
+
// Converts |list_value| to a string, to make it easier for debugging.
std::string ListValueToString(const base::ListValue& list_value) {
std::string json;
@@ -253,7 +276,7 @@ TEST_F(VariationsServiceTest, CreateTrialsFromSeed) {
// Store a seed.
service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), base::Time::Now(), false);
+ std::string(), base::Time::Now(), false, false);
prefs.SetInt64(prefs::kVariationsLastFetchTime,
base::Time::Now().ToInternalValue());
@@ -284,7 +307,7 @@ TEST_F(VariationsServiceTest, CreateTrialsFromSeedNoLastFetchTime) {
// Store a seed. To simulate a first run, |prefs::kVariationsLastFetchTime|
// is left empty.
service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), base::Time::Now(), false);
+ std::string(), base::Time::Now(), false, false);
EXPECT_EQ(0, prefs.GetInt64(prefs::kVariationsLastFetchTime));
// Check that field trials are created from the seed. Since the test study has
@@ -315,7 +338,7 @@ TEST_F(VariationsServiceTest, CreateTrialsFromOutdatedSeed) {
const base::Time seed_date =
base::Time::Now() - base::TimeDelta::FromDays(31);
service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), seed_date, false);
+ std::string(), seed_date, false, false);
prefs.SetInt64(prefs::kVariationsLastFetchTime, seed_date.ToInternalValue());
// Check that field trials are not created from the seed.
@@ -422,8 +445,6 @@ TEST_F(VariationsServiceTest, SeedStoredWhenOKStatus) {
TestVariationsService service(
make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs)),
&prefs);
- service.variations_server_url_ =
- service.GetVariationsServerURL(&prefs, std::string());
service.set_intercepts_fetch(false);
net::TestURLFetcherFactory factory;
@@ -450,12 +471,10 @@ TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) {
TestingPrefServiceSimple prefs;
VariationsService::RegisterPrefs(prefs.registry());
- VariationsService service(
- make_scoped_ptr(new TestVariationsServiceClient()),
+ TestVariationsService service(
make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs)),
- &prefs, NULL, UIStringOverrider());
- service.variations_server_url_ =
- service.GetVariationsServerURL(&prefs, std::string());
+ &prefs);
+ service.set_intercepts_fetch(false);
for (size_t i = 0; i < arraysize(non_ok_status_codes); ++i) {
net::TestURLFetcherFactory factory;
service.DoActualFetch();
@@ -469,6 +488,67 @@ TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) {
}
}
+TEST_F(VariationsServiceTest, RequestGzipCompressedSeed) {
+ TestingPrefServiceSimple prefs;
+ VariationsService::RegisterPrefs(prefs.registry());
+ net::TestURLFetcherFactory factory;
+
+ TestVariationsService service(
+ make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs)),
+ &prefs);
+ service.set_intercepts_fetch(false);
+ service.DoActualFetch();
+
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ net::HttpRequestHeaders headers;
+ fetcher->GetExtraRequestHeaders(&headers);
+ std::string field;
+ ASSERT_TRUE(headers.GetHeader("A-IM", &field));
+ EXPECT_EQ("gzip", field);
+}
+
+TEST_F(VariationsServiceTest, InstanceManipulations) {
+ struct {
+ std::string im;
+ bool delta_compressed;
+ bool gzip_compressed;
+ bool seed_stored;
+ } cases[] = {
+ {"", false, false, true},
+ {"IM:gzip", false, true, true},
+ {"IM:x-bm", true, false, true},
+ {"IM:x-bm,gzip", true, true, true},
+ {"IM: x-bm, gzip", true, true, true},
+ {"IM:gzip,x-bm", false, false, false},
+ {"IM:deflate,x-bm,gzip", false, false, false},
+ };
+
+ TestingPrefServiceSimple prefs;
+ VariationsService::RegisterPrefs(prefs.registry());
+ std::string serialized_seed = SerializeSeed(CreateTestSeed());
+ net::TestURLFetcherFactory factory;
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ TestVariationsService service(
+ make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs)),
+ &prefs);
+ service.set_intercepts_fetch(false);
+ service.DoActualFetch();
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+
+ if (cases[i].im.empty())
+ SimulateServerResponse(net::HTTP_OK, fetcher);
+ else
+ SimulateServerResponseWithHeader(net::HTTP_OK, fetcher, &cases[i].im);
+ fetcher->SetResponseString(serialized_seed);
+ service.OnURLFetchComplete(fetcher);
+
+ EXPECT_EQ(cases[i].seed_stored, service.seed_stored());
+ EXPECT_EQ(cases[i].delta_compressed, service.delta_compressed_seed());
+ EXPECT_EQ(cases[i].gzip_compressed, service.gzip_compressed_seed());
+ }
+}
+
TEST_F(VariationsServiceTest, CountryHeader) {
TestingPrefServiceSimple prefs;
VariationsService::RegisterPrefs(prefs.registry());
@@ -476,8 +556,6 @@ TEST_F(VariationsServiceTest, CountryHeader) {
TestVariationsService service(
make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs)),
&prefs);
- service.variations_server_url_ =
- service.GetVariationsServerURL(&prefs, std::string());
service.set_intercepts_fetch(false);
net::TestURLFetcherFactory factory;
diff --git a/components/variations/variations_seed_store.cc b/components/variations/variations_seed_store.cc
index e20b056..89e1af2 100644
--- a/components/variations/variations_seed_store.cc
+++ b/components/variations/variations_seed_store.cc
@@ -91,6 +91,9 @@ enum VariationsSeedStoreResult {
VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED,
VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY,
VARIATIONS_SEED_STORE_FAILED_DELTA_STORE,
+ VARIATIONS_SEED_STORE_FAILED_UNGZIP,
+ VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS,
+ VARIATIONS_SEED_STORE_FAILED_UNSUPPORTED_SEED_FORMAT,
VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE,
};
@@ -194,14 +197,38 @@ bool VariationsSeedStore::StoreSeedData(
const std::string& country_code,
const base::Time& date_fetched,
bool is_delta_compressed,
+ bool is_gzip_compressed,
variations::VariationsSeed* parsed_seed) {
+ // If the data is gzip compressed, first uncompress it.
+ std::string ungzipped_data;
+ if (is_gzip_compressed) {
+ if (compression::GzipUncompress(data, &ungzipped_data)) {
+ if (ungzipped_data.empty()) {
+ RecordSeedStoreHistogram(
+ VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS);
+ return false;
+ }
+
+ int size_reduction = ungzipped_data.length() - data.length();
+ UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.GzipSize.ReductionPercent",
+ 100 * size_reduction / ungzipped_data.length());
+ UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.GzipSize",
+ data.length() / 1024);
+ } else {
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_UNGZIP);
+ return false;
+ }
+ } else {
+ ungzipped_data = data;
+ }
+
if (!is_delta_compressed) {
const bool result =
- StoreSeedDataNoDelta(data, base64_seed_signature, country_code,
- date_fetched, parsed_seed);
+ StoreSeedDataNoDelta(ungzipped_data, base64_seed_signature,
+ country_code, date_fetched, parsed_seed);
if (result) {
UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.Size",
- data.length() / 1024);
+ ungzipped_data.length() / 1024);
}
return result;
}
@@ -216,7 +243,8 @@ bool VariationsSeedStore::StoreSeedData(
RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED);
return false;
}
- if (!ApplyDeltaPatch(existing_seed_data, data, &updated_seed_data)) {
+ if (!ApplyDeltaPatch(existing_seed_data, ungzipped_data,
+ &updated_seed_data)) {
RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY);
return false;
}
@@ -227,11 +255,11 @@ bool VariationsSeedStore::StoreSeedData(
if (result) {
// Note: |updated_seed_data.length()| is guaranteed to be non-zero, else
// result would be false.
- int size_reduction = updated_seed_data.length() - data.length();
+ int size_reduction = updated_seed_data.length() - ungzipped_data.length();
UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.DeltaSize.ReductionPercent",
100 * size_reduction / updated_seed_data.length());
UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.DeltaSize",
- data.length() / 1024);
+ ungzipped_data.length() / 1024);
} else {
RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_STORE);
}
@@ -347,7 +375,7 @@ bool VariationsSeedStore::StoreSeedDataNoDelta(
const base::Time& date_fetched,
variations::VariationsSeed* parsed_seed) {
if (seed_data.empty()) {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY);
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS);
return false;
}
@@ -450,4 +478,9 @@ bool VariationsSeedStore::ApplyDeltaPatch(const std::string& existing_data,
return true;
}
+void VariationsSeedStore::ReportUnsupportedSeedFormatError() {
+ RecordSeedStoreHistogram(
+ VARIATIONS_SEED_STORE_FAILED_UNSUPPORTED_SEED_FORMAT);
+}
+
} // namespace variations
diff --git a/components/variations/variations_seed_store.h b/components/variations/variations_seed_store.h
index e705a29..cc743d88 100644
--- a/components/variations/variations_seed_store.h
+++ b/components/variations/variations_seed_store.h
@@ -34,7 +34,9 @@ class VariationsSeedStore {
// Stores the given seed |data| (serialized protobuf) to local state, along
// with a base64-encoded digital signature for seed and the date when it was
- // fetched. If |is_delta_compressed| is true, treats |data| as being delta
+ // fetched. If |is_gzip_compressed| is true, treats |data| as being gzip
+ // compressed and decompresses it before any other processing.
+ // If |is_delta_compressed| is true, treats |data| as being delta
// compressed and attempts to decode it first using the store's seed data.
// The actual seed data will be base64 encoded for storage. If the string
// is invalid, the existing prefs are untouched and false is returned.
@@ -46,12 +48,16 @@ class VariationsSeedStore {
const std::string& country_code,
const base::Time& date_fetched,
bool is_delta_compressed,
+ bool is_gzip_compressed,
variations::VariationsSeed* parsed_seed);
// Updates |kVariationsSeedDate| and logs when previous date was from a
// different day.
void UpdateSeedDateAndLogDayChange(const base::Time& server_date_fetched);
+ // Reports to UMA that the seed format specified by the server is unsupported.
+ void ReportUnsupportedSeedFormatError();
+
// Returns the serial number of the last loaded or stored seed.
const std::string& variations_serial_number() const {
return variations_serial_number_;
diff --git a/components/variations/variations_seed_store_unittest.cc b/components/variations/variations_seed_store_unittest.cc
index 5821475..ab7f506 100644
--- a/components/variations/variations_seed_store_unittest.cc
+++ b/components/variations/variations_seed_store_unittest.cc
@@ -24,7 +24,7 @@ class TestVariationsSeedStore : public VariationsSeedStore {
bool StoreSeedForTesting(const std::string& seed_data) {
return StoreSeedData(seed_data, std::string(), std::string(),
- base::Time::Now(), false, nullptr);
+ base::Time::Now(), false, false, nullptr);
}
VariationsSeedStore::VerifySignatureResult VerifySeedSignature(
@@ -211,7 +211,7 @@ TEST(VariationsSeedStoreTest, StoreSeedData_ParsedSeed) {
variations::VariationsSeed parsed_seed;
EXPECT_TRUE(seed_store.StoreSeedData(serialized_seed, std::string(),
std::string(), base::Time::Now(), false,
- &parsed_seed));
+ false, &parsed_seed));
EXPECT_EQ(serialized_seed, SerializeSeed(parsed_seed));
}
@@ -225,7 +225,7 @@ TEST(VariationsSeedStoreTest, StoreSeedData_CountryCode) {
seed.set_country_code("test_country");
EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(),
std::string(), base::Time::Now(), false,
- nullptr));
+ false, nullptr));
EXPECT_EQ("test_country", prefs.GetString(prefs::kVariationsCountry));
// Test with a header value and no seed country.
@@ -233,7 +233,7 @@ TEST(VariationsSeedStoreTest, StoreSeedData_CountryCode) {
seed.clear_country_code();
EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(),
"test_country2", base::Time::Now(),
- false, nullptr));
+ false, false, nullptr));
EXPECT_EQ("test_country2", prefs.GetString(prefs::kVariationsCountry));
// Test with a seed country code and header value.
@@ -241,17 +241,49 @@ TEST(VariationsSeedStoreTest, StoreSeedData_CountryCode) {
seed.set_country_code("test_country3");
EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(),
"test_country4", base::Time::Now(),
- false, nullptr));
+ false, false, nullptr));
EXPECT_EQ("test_country4", prefs.GetString(prefs::kVariationsCountry));
// Test with no country code specified - which should preserve the old value.
seed.clear_country_code();
EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(),
std::string(), base::Time::Now(), false,
- nullptr));
+ false, nullptr));
EXPECT_EQ("test_country4", prefs.GetString(prefs::kVariationsCountry));
}
+TEST(VariationsSeedStoreTest, StoreSeedData_GzippedSeed) {
+ const variations::VariationsSeed seed = CreateTestSeed();
+ const std::string serialized_seed = SerializeSeed(seed);
+ std::string compressed_seed;
+ ASSERT_TRUE(compression::GzipCompress(serialized_seed, &compressed_seed));
+
+ TestingPrefServiceSimple prefs;
+ VariationsSeedStore::RegisterPrefs(prefs.registry());
+ TestVariationsSeedStore seed_store(&prefs);
+
+ variations::VariationsSeed parsed_seed;
+ EXPECT_TRUE(seed_store.StoreSeedData(compressed_seed, std::string(),
+ std::string(), base::Time::Now(), false,
+ true, &parsed_seed));
+ EXPECT_EQ(serialized_seed, SerializeSeed(parsed_seed));
+}
+
+TEST(VariationsSeedStoreTest, StoreSeedData_GzippedEmptySeed) {
+ std::string empty_seed;
+ std::string compressed_seed;
+ ASSERT_TRUE(compression::GzipCompress(empty_seed, &compressed_seed));
+
+ TestingPrefServiceSimple prefs;
+ VariationsSeedStore::RegisterPrefs(prefs.registry());
+ TestVariationsSeedStore seed_store(&prefs);
+
+ variations::VariationsSeed parsed_seed;
+ EXPECT_FALSE(seed_store.StoreSeedData(compressed_seed, std::string(),
+ std::string(), base::Time::Now(), false,
+ true, &parsed_seed));
+}
+
TEST(VariationsSeedStoreTest, VerifySeedSignature) {
// The below seed and signature pair were generated using the server's
// private key.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 67ad828..9aab46f 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -49610,6 +49610,23 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary>
</histogram>
+<histogram name="Variations.StoreSeed.GzipSize" units="KiB">
+ <owner>asvitkine@chromium.org</owner>
+ <summary>
+ Records the size of the gzip-compressed variations seed in KiB.
+ </summary>
+</histogram>
+
+<histogram name="Variations.StoreSeed.GzipSize.ReductionPercent" units="%">
+ <owner>asvitkine@chromium.org</owner>
+ <summary>
+ Records the size of the gzip-compressed variations seed as a percentage of
+ the decoded seed size. Note that variations seed could be first
+ delta-compressed and then gzip-compressed. In this case we record
+ gzip-compressed seed size as a percentage of the delta-compressed seed size.
+ </summary>
+</histogram>
+
<histogram name="Variations.StoreSeed.Size" units="KiB">
<owner>asvitkine@chromium.org</owner>
<summary>
@@ -74342,6 +74359,9 @@ To add a new entry, add it with any value and run test to compute valid value.
<int value="6" label="Failed - Delta: Read Seed"/>
<int value="7" label="Failed - Delta: Apply"/>
<int value="8" label="Failed - Delta: Store Seed"/>
+ <int value="9" label="Failed - Gzip Uncompress Error"/>
+ <int value="10" label="Failed - Empty Gzip Contents"/>
+ <int value="11" label="Failed - Unsupported Seed Format"/>
</enum>
<enum name="VaryType" type="int">