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

Side by Side 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: Address comments. 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (C) 2013 Google Inc. 1 // Copyright (C) 2013 Google Inc.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 #include "retriever.h" 15 #include "retriever.h"
16 16
17 #include <libaddressinput/callback.h> 17 #include <libaddressinput/callback.h>
18 #include <libaddressinput/downloader.h> 18 #include <libaddressinput/downloader.h>
19 #include <libaddressinput/storage.h> 19 #include <libaddressinput/storage.h>
20 #include <libaddressinput/util/basictypes.h> 20 #include <libaddressinput/util/basictypes.h>
21 #include <libaddressinput/util/scoped_ptr.h> 21 #include <libaddressinput/util/scoped_ptr.h>
22 22
23 #include <cassert> 23 #include <cassert>
24 #include <cstddef> 24 #include <cstddef>
25 #include <cstdlib>
26 #include <ctime>
25 #include <map> 27 #include <map>
26 #include <string> 28 #include <string>
27 #include <utility>
28 29
29 #include "fallback_data_store.h" 30 #include "fallback_data_store.h"
31 #include "time_to_string.h"
32 #include "util/md5.h"
30 #include "util/stl_util.h" 33 #include "util/stl_util.h"
31 34
32 namespace i18n { 35 namespace i18n {
33 namespace addressinput { 36 namespace addressinput {
34 37
38 namespace {
39
40 // The number of seconds after which data is considered stale. The staleness
41 // threshold is 30 days:
42 // 30 days *
43 // 24 hours per day *
44 // 60 minutes per hour *
45 // 60 seconds per minute.
46 static const double kStaleDataAgeInSeconds = 30.0 * 24.0 * 60.0 * 60.0;
47
48 // The prefix for the timestamp line in the header.
49 const char kTimestampPrefix[] = "timestamp=";
50 const size_t kTimestampPrefixLength = sizeof kTimestampPrefix - 1;
51
52 // The prefix for the checksum line in the header.
53 const char kChecksumPrefix[] = "checksum=";
54 const size_t kChecksumPrefixLength = sizeof kChecksumPrefix - 1;
55
56 // The separator between lines of header and data.
57 const char kSeparator = '\n';
58
59 // Places the header value into |header_value| parameter and erases the header
60 // from |data|. Returns |true| if the header format is valid.
61 bool UnwrapHeader(const char* header_prefix,
Evan Stade 2014/01/23 01:44:50 nit: put this about Unwrap also, s/UnwrapHeader/E
please use gerrit instead 2014/01/23 23:11:05 Done.
62 size_t header_prefix_length,
63 std::string* data,
64 std::string* header_value) {
65 assert(header_prefix != NULL);
66 assert(data != NULL);
67 assert(header_value != NULL);
68
69 if (data->compare(
70 0, header_prefix_length, header_prefix, header_prefix_length) != 0) {
71 return false;
72 }
73
74 std::string::size_type separator_position =
75 data->find(kSeparator, header_prefix_length);
76 if (separator_position == std::string::npos) {
77 return false;
78 }
79
80 header_value->assign(
81 *data, header_prefix_length, separator_position - header_prefix_length);
82 data->erase(0, separator_position + 1);
83
84 return true;
85 }
86
87 // Returns |data| with attached checksum and current timestamp. Format:
88 //
89 // timestamp=<timestamp>
90 // checksum=<checksum>
91 // <data>
92 //
93 // The timestamp is the time_t that was returned from time(NULL) function. The
94 // timestamp does not need to be portable because it is written and read only by
95 // Retriever. The value is somewhat human-readable: it is the number of seconds
96 // since the epoch.
97 //
98 // The checksum is the 32-character hexadecimal MD5 checksum of <data>. It is
99 // meant to protect from random file changes on disk.
100 std::string Wrap(const std::string& data) {
Evan Stade 2014/01/23 01:44:50 s/Wrap/PrependTimestamp
please use gerrit instead 2014/01/23 23:11:05 Done.
101 std::string wrapped;
102 wrapped.append(kTimestampPrefix, kTimestampPrefixLength);
103 wrapped.append(TimeToString(time(NULL)));
104 wrapped.push_back(kSeparator);
105
106 wrapped.append(kChecksumPrefix, kChecksumPrefixLength);
107 wrapped.append(MD5String(data));
108 wrapped.push_back(kSeparator);
109 wrapped.append(data);
110
111 return wrapped;
112 }
113
114 // Strips out the timestamp and checksum from |data|. Validates the checksum.
115 // Compares the parsed timestamp with current time and saves the difference
116 // into |age_in_seconds|.
117 //
118 // The parameters should not be NULL. Does not take ownership of its
119 // parameters.
120 //
121 // Returns |true| if |data| is correctly formatted and has the correct
122 // checksum.
123 bool Unwrap(std::string* data, double* age_in_seconds) {
Evan Stade 2014/01/23 01:44:50 I'd prefer if there were separate in and out param
Evan Stade 2014/01/23 01:44:50 s/Unwrap/VerifyAndExtractTimestamp
please use gerrit instead 2014/01/23 23:11:05 Done.
please use gerrit instead 2014/01/23 23:11:05 Done.
124 assert(data != NULL);
125 assert(age_in_seconds != NULL);
126
127 std::string timestamp_string;
128 if (!UnwrapHeader(
129 kTimestampPrefix, kTimestampPrefixLength, data, &timestamp_string)) {
130 return false;
131 }
132
133 time_t timestamp = atol(timestamp_string.c_str());
134 if (timestamp < 0) {
135 return false;
136 }
137
138 *age_in_seconds = difftime(time(NULL), timestamp);
139 if (*age_in_seconds < 0.0) {
140 return false;
141 }
142
143 std::string checksum;
144 if (!UnwrapHeader(kChecksumPrefix, kChecksumPrefixLength, data, &checksum)) {
145 return false;
146 }
147
148 return checksum == MD5String(*data);
149 }
150
151 } // namespace
152
35 Retriever::Retriever(const std::string& validation_data_url, 153 Retriever::Retriever(const std::string& validation_data_url,
36 scoped_ptr<Downloader> downloader, 154 scoped_ptr<Downloader> downloader,
37 scoped_ptr<Storage> storage) 155 scoped_ptr<Storage> storage)
38 : validation_data_url_(validation_data_url), 156 : validation_data_url_(validation_data_url),
39 downloader_(downloader.Pass()), 157 downloader_(downloader.Pass()),
40 storage_(storage.Pass()) { 158 storage_(storage.Pass()),
159 stale_data_() {
41 assert(validation_data_url_.length() > 0); 160 assert(validation_data_url_.length() > 0);
42 assert(validation_data_url_[validation_data_url_.length() - 1] == '/'); 161 assert(validation_data_url_[validation_data_url_.length() - 1] == '/');
43 assert(storage_ != NULL); 162 assert(storage_ != NULL);
44 assert(downloader_ != NULL); 163 assert(downloader_ != NULL);
45 } 164 }
46 165
47 Retriever::~Retriever() { 166 Retriever::~Retriever() {
48 STLDeleteValues(&requests_); 167 STLDeleteValues(&requests_);
49 } 168 }
50 169
51 void Retriever::Retrieve(const std::string& key, 170 void Retriever::Retrieve(const std::string& key,
52 scoped_ptr<Callback> retrieved) { 171 scoped_ptr<Callback> retrieved) {
53 std::map<std::string, Callback*>::iterator request_it = 172 std::map<std::string, Callback*>::iterator request_it =
54 requests_.find(key); 173 requests_.find(key);
55 if (request_it != requests_.end()) { 174 if (request_it != requests_.end()) {
56 // Abandon a previous request. 175 // Abandon a previous request.
57 delete request_it->second; 176 delete request_it->second;
58 requests_.erase(request_it); 177 requests_.erase(request_it);
59 } 178 }
60 179
61 requests_[key] = retrieved.release(); 180 requests_[key] = retrieved.release();
62 storage_->Get(key, 181 storage_->Get(key,
63 BuildCallback(this, &Retriever::OnDataRetrievedFromStorage)); 182 BuildCallback(this, &Retriever::OnDataRetrievedFromStorage));
64 } 183 }
65 184
66 void Retriever::OnDataRetrievedFromStorage(bool success, 185 void Retriever::OnDataRetrievedFromStorage(bool success,
67 const std::string& key, 186 const std::string& key,
68 const std::string& stored_data) { 187 const std::string& stored_data) {
69 // TODO(rouslan): Add validation for data integrity and freshness. If a 188 std::string unwrapped = stored_data;
70 // download fails, then it's OK to use stale data. 189 double age_in_seconds = 0.0;
71 if (success) { 190 bool valid_format = Unwrap(&unwrapped, &age_in_seconds);
191 if (success && valid_format && age_in_seconds < kStaleDataAgeInSeconds) {
Evan Stade 2014/01/23 01:44:50 if (!success) you shouldn't call Unwrap
please use gerrit instead 2014/01/23 23:11:05 Done.
72 scoped_ptr<Callback> retrieved = GetCallbackForKey(key); 192 scoped_ptr<Callback> retrieved = GetCallbackForKey(key);
73 if (retrieved != NULL) { 193 if (retrieved != NULL) {
74 (*retrieved)(success, key, stored_data); 194 (*retrieved)(success, key, unwrapped);
75 } 195 }
76 } else { 196 } else {
Evan Stade 2014/01/23 01:44:50 nit: prefer early return over else {} that goes to
please use gerrit instead 2014/01/23 23:11:05 Done.
197 if (success && valid_format) {
198 stale_data_[key] = unwrapped;
199 }
77 downloader_->Download(GetUrlForKey(key), 200 downloader_->Download(GetUrlForKey(key),
78 BuildCallback(this, &Retriever::OnDownloaded)); 201 BuildCallback(this, &Retriever::OnDownloaded));
79 } 202 }
80 } 203 }
81 204
82 void Retriever::OnDownloaded(bool success, 205 void Retriever::OnDownloaded(bool success,
83 const std::string& url, 206 const std::string& url,
84 const std::string& downloaded_data) { 207 const std::string& downloaded_data) {
85 const std::string& key = GetKeyForUrl(url); 208 const std::string& key = GetKeyForUrl(url);
86 std::string response; 209 std::string response;
210 std::map<std::string, std::string>::iterator stale_data_it =
211 stale_data_.find(key);
212
87 if (success) { 213 if (success) {
88 storage_->Put(key, downloaded_data); 214 storage_->Put(key, Wrap(downloaded_data));
89 response = downloaded_data; 215 response = downloaded_data;
Evan Stade 2014/01/23 01:44:50 unnecessary copy (potentially quite large)
please use gerrit instead 2014/01/23 23:11:05 Done.
216 } else if (stale_data_it != stale_data_.end()) {
217 success = true;
218 response = stale_data_it->second;
Evan Stade 2014/01/23 01:44:50 unnecessary copy (potentially quite large)
please use gerrit instead 2014/01/23 23:11:05 Done.
90 } else { 219 } else {
91 success = FallbackDataStore::Get(key, &response); 220 success = FallbackDataStore::Get(key, &response);
92 } 221 }
93 222
223 if (stale_data_it != stale_data_.end()) {
224 stale_data_.erase(stale_data_it);
225 }
226
94 scoped_ptr<Callback> retrieved = GetCallbackForKey(key); 227 scoped_ptr<Callback> retrieved = GetCallbackForKey(key);
95 if (retrieved != NULL) { 228 if (retrieved != NULL) {
96 (*retrieved)(success, key, response); 229 (*retrieved)(success, key, response);
97 } 230 }
98 } 231 }
99 232
100 std::string Retriever::GetUrlForKey(const std::string& key) const { 233 std::string Retriever::GetUrlForKey(const std::string& key) const {
101 return validation_data_url_ + key; 234 return validation_data_url_ + key;
102 } 235 }
103 236
104 std::string Retriever::GetKeyForUrl(const std::string& url) const { 237 std::string Retriever::GetKeyForUrl(const std::string& url) const {
105 if (url.compare(0, validation_data_url_.length(), validation_data_url_) == 0) 238 if (url.compare(0, validation_data_url_.length(), validation_data_url_) == 0)
106 return url.substr(validation_data_url_.length()); 239 return url.substr(validation_data_url_.length());
107 240
108 return std::string(); 241 return std::string();
109 } 242 }
110 243
111 bool Retriever::IsValidationDataUrl(const std::string& url) const { 244 bool Retriever::IsValidationDataUrl(const std::string& url) const {
112 return 245 return
113 url.compare(0, validation_data_url_.length(), validation_data_url_) == 0; 246 url.compare(0, validation_data_url_.length(), validation_data_url_) == 0;
114 } 247 }
115 248
116 scoped_ptr<Retriever::Callback> Retriever::GetCallbackForKey( 249 scoped_ptr<Retriever::Callback> Retriever::GetCallbackForKey(
117 const std::string& key) { 250 const std::string& key) {
118 std::map<std::string, Callback*>::iterator iter = 251 std::map<std::string, Callback*>::iterator iter =
119 requests_.find(key); 252 requests_.find(key);
120 if (iter == requests_.end()) { 253 if (iter == requests_.end()) {
121 // An abandonened request. 254 // An abandonened request.
Evan Stade 2014/01/23 01:44:50 nit: spelling
please use gerrit instead 2014/01/23 23:11:05 Done.
122 return scoped_ptr<Callback>(); 255 return scoped_ptr<Callback>();
123 } 256 }
124 scoped_ptr<Callback> callback(iter->second); 257 scoped_ptr<Callback> callback(iter->second);
125 requests_.erase(iter); 258 requests_.erase(iter);
126 return callback.Pass(); 259 return callback.Pass();
127 } 260 }
128 261
129 } // namespace addressinput 262 } // namespace addressinput
130 } // namespace i18n 263 } // namespace i18n
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698