| 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,
|
| + ×tamp_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
|
|
|