Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Unified Diff: chrome/browser/metrics/variations/variations_seed_store.cc

Issue 1200233005: Support delta-compressed variations server responses. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixing missing return in !is_compressed case. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/metrics/variations/variations_seed_store.cc
diff --git a/chrome/browser/metrics/variations/variations_seed_store.cc b/chrome/browser/metrics/variations/variations_seed_store.cc
index 6e83ab6bcfc8d25a3ed1723ac434431617503e0d..b4e51a8c9c246b613bb84f7ad92a1bdf199815d7 100644
--- a/chrome/browser/metrics/variations/variations_seed_store.cc
+++ b/chrome/browser/metrics/variations/variations_seed_store.cc
@@ -6,6 +6,7 @@
#include "base/base64.h"
#include "base/metrics/histogram_macros.h"
+#include "base/numerics/safe_math.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/sha1.h"
@@ -14,6 +15,7 @@
#include "components/metrics/compression_utils.h"
#include "components/variations/proto/variations_seed.pb.h"
#include "crypto/signature_verifier.h"
+#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
namespace chrome_variations {
@@ -82,10 +84,17 @@ enum VariationsSeedStoreResult {
VARIATIONS_SEED_STORE_FAILED_PARSE,
VARIATIONS_SEED_STORE_FAILED_SIGNATURE,
VARIATIONS_SEED_STORE_FAILED_GZIP,
+ // DELTA_COUNT is not so much a result of the seed store, but rather counting
+ // the number of delta-compressed seeds the SeedStore() function saw. Kept in
+ // the same histogram for convenience of comparing against the other values.
+ VARIATIONS_SEED_STORE_DELTA_COUNT,
+ VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED,
+ VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY,
+ VARIATIONS_SEED_STORE_FAILED_DELTA_STORE,
VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE,
};
-void RecordVariationsSeedStoreHistogram(VariationsSeedStoreResult result) {
+void RecordSeedStoreHistogram(VariationsSeedStoreResult result) {
UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result,
VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE);
}
@@ -131,7 +140,7 @@ VariationsSeedDateChangeState GetSeedDateChangeState(
} // namespace
VariationsSeedStore::VariationsSeedStore(PrefService* local_state)
- : local_state_(local_state) {
+ : local_state_(local_state), seed_has_country_code_(false) {
}
VariationsSeedStore::~VariationsSeedStore() {
@@ -174,24 +183,78 @@ bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) {
local_state_->SetString(prefs::kVariationsCountry, seed->country_code());
}
variations_serial_number_ = seed->serial_number();
+ seed_has_country_code_ = seed->has_country_code();
RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY);
return true;
}
bool VariationsSeedStore::StoreSeedData(
+ const std::string& data,
+ const std::string& base64_seed_signature,
+ const std::string& country_code,
+ const base::Time& date_fetched,
+ bool is_delta_compressed,
+ variations::VariationsSeed* parsed_seed) {
+ if (!is_delta_compressed) {
+ const bool result =
+ StoreSeedDataNoDelta(data, base64_seed_signature, country_code,
+ date_fetched, parsed_seed);
+ if (result) {
+ UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.Size",
+ data.length() / 1024);
+ }
+ return result;
+ }
+
+ // If the data is delta compressed, first decode it.
+ DCHECK(invalid_base64_signature_.empty());
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_DELTA_COUNT);
+
+ std::string existing_seed_data;
+ std::string updated_seed_data;
+ if (!ReadSeedData(&existing_seed_data)) {
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED);
+ return false;
+ }
+ if (!ApplyDeltaPatch(existing_seed_data, data, &updated_seed_data)) {
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY);
+ return false;
+ }
+
+ const bool result =
+ StoreSeedDataNoDelta(updated_seed_data, base64_seed_signature,
+ country_code, date_fetched, parsed_seed);
+ 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();
+ UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.DeltaSize.ReductionPercent",
+ 100 * size_reduction / updated_seed_data.length());
+ UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.DeltaSize",
+ data.length() / 1024);
+ } else {
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_STORE);
+ }
+ return result;
+}
+
+// TODO(asvitkine): Move this method down to match declaration order in a
+// follow-up CL.
+bool VariationsSeedStore::StoreSeedDataNoDelta(
const std::string& seed_data,
const std::string& base64_seed_signature,
+ const std::string& country_code,
const base::Time& date_fetched,
variations::VariationsSeed* parsed_seed) {
if (seed_data.empty()) {
- RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY);
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY);
return false;
}
// Only store the seed data if it parses correctly.
variations::VariationsSeed seed;
if (!seed.ParseFromString(seed_data)) {
- RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE);
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE);
return false;
}
@@ -201,8 +264,7 @@ bool VariationsSeedStore::StoreSeedData(
UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result,
VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
- RecordVariationsSeedStoreHistogram(
- VARIATIONS_SEED_STORE_FAILED_SIGNATURE);
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_SIGNATURE);
return false;
}
}
@@ -210,7 +272,7 @@ bool VariationsSeedStore::StoreSeedData(
// Compress the seed before base64-encoding and storing.
std::string compressed_seed_data;
if (!metrics::GzipCompress(seed_data, &compressed_seed_data)) {
- RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP);
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP);
return false;
}
@@ -222,7 +284,11 @@ bool VariationsSeedStore::StoreSeedData(
local_state_->ClearPref(prefs::kVariationsSeed);
// Update the saved country code only if one was returned from the server.
- if (seed.has_country_code())
+ // Prefer the country code that was transmitted in the header over the one in
+ // the seed (which is deprecated).
+ if (!country_code.empty())
+ local_state_->SetString(prefs::kVariationsCountry, country_code);
+ else if (seed.has_country_code())
local_state_->SetString(prefs::kVariationsCountry, seed.country_code());
local_state_->SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
@@ -233,7 +299,7 @@ bool VariationsSeedStore::StoreSeedData(
if (parsed_seed)
seed.Swap(parsed_seed);
- RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS);
+ RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS);
return true;
}
@@ -257,6 +323,11 @@ void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
server_date_fetched.ToInternalValue());
}
+
+std::string VariationsSeedStore::GetInvalidSignature() const {
+ return invalid_base64_signature_;
+}
+
// static
void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string());
@@ -335,8 +406,51 @@ VariationsSeedStore::VerifySeedSignature(
return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
}
-std::string VariationsSeedStore::GetInvalidSignature() const {
- return invalid_base64_signature_;
+// static
+bool VariationsSeedStore::ApplyDeltaPatch(const std::string& existing_data,
+ const std::string& patch,
+ std::string* output) {
+ output->clear();
+
+ google::protobuf::io::CodedInputStream in(
+ reinterpret_cast<const uint8*>(patch.data()), patch.length());
+ // Temporary string declared outside the loop so it can be re-used between
+ // different iterations (rather than allocating new ones).
+ std::string temp;
+
+ const uint32 existing_data_size = static_cast<uint32>(existing_data.size());
+ while (in.CurrentPosition() != static_cast<int>(patch.length())) {
+ uint32 value;
+ if (!in.ReadVarint32(&value))
+ return false;
+
+ if (value != 0) {
+ // A non-zero value indicates the number of bytes to copy from the patch
+ // stream to the output.
+
+ // No need to guard against bad data (i.e. very large |value|) because the
+ // call below will fail if |value| is greater than the size of the patch.
+ if (!in.ReadString(&temp, value))
+ return false;
+ output->append(temp);
+ } else {
+ // Otherwise, when it's zero, it indicates that it's followed by a pair of
+ // numbers - |offset| and |length| that specify a range of data to copy
+ // from |existing_data|.
+ uint32 offset;
+ uint32 length;
+ if (!in.ReadVarint32(&offset) || !in.ReadVarint32(&length))
+ return false;
+
+ // Check for |offset + length| being out of range and for overflow.
+ base::CheckedNumeric<uint32> end_offset(offset);
+ end_offset += length;
+ if (!end_offset.IsValid() || end_offset.ValueOrDie() > existing_data_size)
+ return false;
+ output->append(existing_data, offset, length);
+ }
+ }
+ return true;
}
} // namespace chrome_variations

Powered by Google App Engine
This is Rietveld 408576698