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

Unified Diff: third_party/libaddressinput/chromium/cpp/src/retriever.cc

Issue 144353002: [rac] Use stale libaddressinput data if download fails (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: No else after return Created 6 years, 11 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: third_party/libaddressinput/chromium/cpp/src/retriever.cc
diff --git a/third_party/libaddressinput/chromium/cpp/src/retriever.cc b/third_party/libaddressinput/chromium/cpp/src/retriever.cc
index eebba3eb2d78b9b5d590f6aa8a4ba2819d39d7c1..8d18f97b5ae75545f2cbdf7bc8ac2b4fae43d032 100644
--- a/third_party/libaddressinput/chromium/cpp/src/retriever.cc
+++ b/third_party/libaddressinput/chromium/cpp/src/retriever.cc
@@ -22,22 +22,144 @@
#include <cassert>
#include <cstddef>
+#include <cstdlib>
+#include <ctime>
#include <map>
#include <string>
-#include <utility>
#include "fallback_data_store.h"
+#include "time_to_string.h"
+#include "util/md5.h"
#include "util/stl_util.h"
namespace i18n {
namespace addressinput {
+namespace {
+
+// The number of seconds after which data is considered stale. The staleness
+// threshold is 30 days:
+// 30 days *
+// 24 hours per day *
+// 60 minutes per hour *
+// 60 seconds per minute.
+static const double kStaleDataAgeInSeconds = 30.0 * 24.0 * 60.0 * 60.0;
+
+// The prefix for the timestamp line in the header.
+const char kTimestampPrefix[] = "timestamp=";
+const size_t kTimestampPrefixLength = sizeof kTimestampPrefix - 1;
+
+// The prefix for the checksum line in the header.
+const char kChecksumPrefix[] = "checksum=";
+const size_t kChecksumPrefixLength = sizeof kChecksumPrefix - 1;
+
+// The separator between lines of header and data.
+const char kSeparator = '\n';
+
+// Returns |data| with attached checksum and current timestamp. Format:
+//
+// timestamp=<timestamp>
+// checksum=<checksum>
+// <data>
+//
+// The timestamp is the time_t that was returned from time(NULL) function. The
+// timestamp does not need to be portable because it is written and read only by
+// Retriever. The value is somewhat human-readable: it is the number of seconds
+// since the epoch.
+//
+// The checksum is the 32-character hexadecimal MD5 checksum of <data>. It is
+// meant to protect from random file changes on disk.
+std::string PrependTimestamp(const std::string& data) {
+ std::string wrapped;
+ wrapped.append(kTimestampPrefix, kTimestampPrefixLength);
+ wrapped.append(TimeToString(time(NULL)));
+ wrapped.push_back(kSeparator);
+
+ wrapped.append(kChecksumPrefix, kChecksumPrefixLength);
+ wrapped.append(MD5String(data));
+ wrapped.push_back(kSeparator);
+ wrapped.append(data);
+
+ return wrapped;
+}
+
+// Places the header value into |header_value| parameter and the rest of the
+// data into |data| parameter. Returns |true| if the header format is valid.
+bool ExtractHeader(const std::string& header_and_data,
+ const char* header_prefix,
+ size_t header_prefix_length,
+ std::string* header_value,
+ std::string* data) {
+ assert(header_prefix != NULL);
+ assert(header_value != NULL);
+ assert(data != NULL);
+
+ if (header_and_data.compare(
+ 0, header_prefix_length, header_prefix, header_prefix_length) != 0) {
+ return false;
+ }
+
+ std::string::size_type separator_position =
+ header_and_data.find(kSeparator, header_prefix_length);
+ if (separator_position == std::string::npos) {
+ return false;
+ }
+
+ data->assign(header_and_data, separator_position + 1, std::string::npos);
+ header_value->assign(header_and_data, header_prefix_length,
+ separator_position - header_prefix_length);
+ return true;
+}
+
+// Strips out the timestamp and checksum from |header_and_data|. Validates the
+// checksum. Saves the header-less data into |data|. Compares the parsed
+// timestamp with current time and saves the difference into |age_in_seconds|.
+//
+// The parameters should not be NULL. Does not take ownership of its parameters.
+//
+// Returns |true| if |header_and_data| is correctly formatted and has the
+// correct checksum.
+bool VerifyAndExtractTimestamp(const std::string& header_and_data,
+ std::string* data,
+ double* age_in_seconds) {
+ assert(data != NULL);
+ assert(age_in_seconds != NULL);
+
+ std::string timestamp_string;
+ std::string checksum_and_data;
+ if (!ExtractHeader(header_and_data, kTimestampPrefix, kTimestampPrefixLength,
+ &timestamp_string, &checksum_and_data)) {
+ return false;
+ }
+
+ time_t timestamp = atol(timestamp_string.c_str());
+ if (timestamp < 0) {
+ return false;
+ }
+
+ *age_in_seconds = difftime(time(NULL), timestamp);
+ if (*age_in_seconds < 0.0) {
+ return false;
+ }
+
+ std::string checksum;
+ if (!ExtractHeader(checksum_and_data, kChecksumPrefix, kChecksumPrefixLength,
+ &checksum, data)) {
+ return false;
+ }
+
+ return checksum == MD5String(*data);
+}
+
+} // namespace
+
Retriever::Retriever(const std::string& validation_data_url,
scoped_ptr<Downloader> downloader,
scoped_ptr<Storage> storage)
: validation_data_url_(validation_data_url),
downloader_(downloader.Pass()),
- storage_(storage.Pass()) {
+ storage_(storage.Pass()),
+ stale_data_() {
assert(validation_data_url_.length() > 0);
assert(validation_data_url_[validation_data_url_.length() - 1] == '/');
assert(storage_ != NULL);
@@ -66,34 +188,42 @@ void Retriever::Retrieve(const std::string& key,
void Retriever::OnDataRetrievedFromStorage(bool success,
const std::string& key,
const std::string& stored_data) {
- // TODO(rouslan): Add validation for data integrity and freshness. If a
- // download fails, then it's OK to use stale data.
if (success) {
- scoped_ptr<Callback> retrieved = GetCallbackForKey(key);
- if (retrieved != NULL) {
- (*retrieved)(success, key, stored_data);
+ std::string unwrapped;
+ double age_in_seconds = 0.0;
+ if (VerifyAndExtractTimestamp(stored_data, &unwrapped, &age_in_seconds)) {
+ if (age_in_seconds < kStaleDataAgeInSeconds) {
+ InvokeCallbackForKey(key, success, unwrapped);
+ return;
+ }
+ stale_data_[key] = unwrapped;
}
- } else {
- downloader_->Download(GetUrlForKey(key),
- BuildCallback(this, &Retriever::OnDownloaded));
}
+
+ downloader_->Download(GetUrlForKey(key),
+ BuildCallback(this, &Retriever::OnDownloaded));
}
void Retriever::OnDownloaded(bool success,
const std::string& url,
const std::string& downloaded_data) {
const std::string& key = GetKeyForUrl(url);
- std::string response;
+ std::map<std::string, std::string>::iterator stale_data_it =
+ stale_data_.find(key);
+
if (success) {
- storage_->Put(key, downloaded_data);
- response = downloaded_data;
+ storage_->Put(key, PrependTimestamp(downloaded_data));
+ InvokeCallbackForKey(key, success, downloaded_data);
+ } else if (stale_data_it != stale_data_.end()) {
+ InvokeCallbackForKey(key, true, stale_data_it->second);
} else {
- success = FallbackDataStore::Get(key, &response);
+ std::string fallback;
+ success = FallbackDataStore::Get(key, &fallback);
+ InvokeCallbackForKey(key, success, fallback);
}
- scoped_ptr<Callback> retrieved = GetCallbackForKey(key);
- if (retrieved != NULL) {
- (*retrieved)(success, key, response);
+ if (stale_data_it != stale_data_.end()) {
+ stale_data_.erase(stale_data_it);
}
}
@@ -102,10 +232,10 @@ std::string Retriever::GetUrlForKey(const std::string& key) const {
}
std::string Retriever::GetKeyForUrl(const std::string& url) const {
- if (url.compare(0, validation_data_url_.length(), validation_data_url_) == 0)
- return url.substr(validation_data_url_.length());
-
- return std::string();
+ return
+ url.compare(0, validation_data_url_.length(), validation_data_url_) == 0
+ ? url.substr(validation_data_url_.length())
+ : std::string();
}
bool Retriever::IsValidationDataUrl(const std::string& url) const {
@@ -113,17 +243,21 @@ bool Retriever::IsValidationDataUrl(const std::string& url) const {
url.compare(0, validation_data_url_.length(), validation_data_url_) == 0;
}
-scoped_ptr<Retriever::Callback> Retriever::GetCallbackForKey(
- const std::string& key) {
+void Retriever::InvokeCallbackForKey(const std::string& key,
+ bool success,
+ const std::string& data) {
std::map<std::string, Callback*>::iterator iter =
requests_.find(key);
if (iter == requests_.end()) {
- // An abandonened request.
- return scoped_ptr<Callback>();
+ // An abandoned request.
+ return;
}
scoped_ptr<Callback> callback(iter->second);
requests_.erase(iter);
- return callback.Pass();
+ if (callback == NULL) {
+ return;
+ }
+ (*callback)(success, key, data);
}
} // namespace addressinput

Powered by Google App Engine
This is Rietveld 408576698