Index: components/variations/service/variations_service.cc |
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc |
index 2607ead57724737db0340ed82737a38d348fc3c2..8d3ced5c602e0ab92fe427cf97b1703271c0a6cd 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(); |