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

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 // Returns |data| with attached checksum and current timestamp. Format:
60 //
61 // timestamp=<timestamp>
62 // checksum=<checksum>
63 // <data>
64 //
65 // The timestamp is the time_t that was returned from time(NULL) function. The
66 // timestamp does not need to be portable because it is written and read only by
67 // Retriever. The value is somewhat human-readable: it is the number of seconds
68 // since the epoch.
69 //
70 // The checksum is the 32-character hexadecimal MD5 checksum of <data>. It is
71 // meant to protect from random file changes on disk.
72 std::string PrependTimestamp(const std::string& data) {
73 std::string wrapped;
74 wrapped.append(kTimestampPrefix, kTimestampPrefixLength);
75 wrapped.append(TimeToString(time(NULL)));
76 wrapped.push_back(kSeparator);
77
78 wrapped.append(kChecksumPrefix, kChecksumPrefixLength);
79 wrapped.append(MD5String(data));
80 wrapped.push_back(kSeparator);
81 wrapped.append(data);
82
83 return wrapped;
84 }
85
86 // Places the header value into |header_value| parameter and the rest of the
87 // data into |data| parameter. Returns |true| if the header format is valid.
88 bool ExtractHeader(const std::string& header_and_data,
89 const char* header_prefix,
90 size_t header_prefix_length,
91 std::string* header_value,
92 std::string* data) {
93 assert(header_prefix != NULL);
94 assert(header_value != NULL);
95 assert(data != NULL);
96
97 if (header_and_data.compare(
98 0, header_prefix_length, header_prefix, header_prefix_length) != 0) {
99 return false;
100 }
101
102 std::string::size_type separator_position =
103 header_and_data.find(kSeparator, header_prefix_length);
104 if (separator_position == std::string::npos) {
105 return false;
106 }
107
108 data->assign(header_and_data, separator_position + 1, std::string::npos);
109 header_value->assign(header_and_data, header_prefix_length,
110 separator_position - header_prefix_length);
111 return true;
112 }
113
114 // Strips out the timestamp and checksum from |header_and_data|. Validates the
115 // checksum. Saves the header-less data into |data|. Compares the parsed
116 // timestamp with current time and saves the difference into |age_in_seconds|.
117 //
118 // The parameters should not be NULL. Does not take ownership of its parameters.
119 //
120 // Returns |true| if |header_and_data| is correctly formatted and has the
121 // correct checksum.
122 bool VerifyAndExtractTimestamp(const std::string& header_and_data,
123 std::string* data,
124 double* age_in_seconds) {
125 assert(data != NULL);
126 assert(age_in_seconds != NULL);
127
128 std::string timestamp_string;
129 std::string checksum_and_data;
130 if (!ExtractHeader(header_and_data, kTimestampPrefix, kTimestampPrefixLength,
131 &timestamp_string, &checksum_and_data)) {
132 return false;
133 }
134
135 time_t timestamp = atol(timestamp_string.c_str());
136 if (timestamp < 0) {
137 return false;
138 }
139
140 *age_in_seconds = difftime(time(NULL), timestamp);
141 if (*age_in_seconds < 0.0) {
142 return false;
143 }
144
145 std::string checksum;
146 if (!ExtractHeader(checksum_and_data, kChecksumPrefix, kChecksumPrefixLength,
147 &checksum, data)) {
148 return false;
149 }
150
151 return checksum == MD5String(*data);
152 }
153
154 } // namespace
155
35 Retriever::Retriever(const std::string& validation_data_url, 156 Retriever::Retriever(const std::string& validation_data_url,
36 scoped_ptr<Downloader> downloader, 157 scoped_ptr<Downloader> downloader,
37 scoped_ptr<Storage> storage) 158 scoped_ptr<Storage> storage)
38 : validation_data_url_(validation_data_url), 159 : validation_data_url_(validation_data_url),
39 downloader_(downloader.Pass()), 160 downloader_(downloader.Pass()),
40 storage_(storage.Pass()) { 161 storage_(storage.Pass()),
162 stale_data_() {
41 assert(validation_data_url_.length() > 0); 163 assert(validation_data_url_.length() > 0);
42 assert(validation_data_url_[validation_data_url_.length() - 1] == '/'); 164 assert(validation_data_url_[validation_data_url_.length() - 1] == '/');
43 assert(storage_ != NULL); 165 assert(storage_ != NULL);
44 assert(downloader_ != NULL); 166 assert(downloader_ != NULL);
45 } 167 }
46 168
47 Retriever::~Retriever() { 169 Retriever::~Retriever() {
48 STLDeleteValues(&requests_); 170 STLDeleteValues(&requests_);
49 } 171 }
50 172
51 void Retriever::Retrieve(const std::string& key, 173 void Retriever::Retrieve(const std::string& key,
52 scoped_ptr<Callback> retrieved) { 174 scoped_ptr<Callback> retrieved) {
53 std::map<std::string, Callback*>::iterator request_it = 175 std::map<std::string, Callback*>::iterator request_it =
54 requests_.find(key); 176 requests_.find(key);
55 if (request_it != requests_.end()) { 177 if (request_it != requests_.end()) {
56 // Abandon a previous request. 178 // Abandon a previous request.
57 delete request_it->second; 179 delete request_it->second;
58 requests_.erase(request_it); 180 requests_.erase(request_it);
59 } 181 }
60 182
61 requests_[key] = retrieved.release(); 183 requests_[key] = retrieved.release();
62 storage_->Get(key, 184 storage_->Get(key,
63 BuildCallback(this, &Retriever::OnDataRetrievedFromStorage)); 185 BuildCallback(this, &Retriever::OnDataRetrievedFromStorage));
64 } 186 }
65 187
66 void Retriever::OnDataRetrievedFromStorage(bool success, 188 void Retriever::OnDataRetrievedFromStorage(bool success,
67 const std::string& key, 189 const std::string& key,
68 const std::string& stored_data) { 190 const std::string& stored_data) {
69 // TODO(rouslan): Add validation for data integrity and freshness. If a
70 // download fails, then it's OK to use stale data.
71 if (success) { 191 if (success) {
72 scoped_ptr<Callback> retrieved = GetCallbackForKey(key); 192 std::string unwrapped;
73 if (retrieved != NULL) { 193 double age_in_seconds = 0.0;
74 (*retrieved)(success, key, stored_data); 194 if (VerifyAndExtractTimestamp(stored_data, &unwrapped, &age_in_seconds)) {
195 if (age_in_seconds < kStaleDataAgeInSeconds) {
196 InvokeCallbackForKey(key, success, unwrapped);
197 return;
198 } else {
Evan Stade 2014/01/23 23:50:20 no else after return
please use gerrit instead 2014/01/23 23:54:56 Done.
199 stale_data_[key] = unwrapped;
200 }
75 } 201 }
76 } else {
77 downloader_->Download(GetUrlForKey(key),
78 BuildCallback(this, &Retriever::OnDownloaded));
79 } 202 }
203
204 downloader_->Download(GetUrlForKey(key),
205 BuildCallback(this, &Retriever::OnDownloaded));
80 } 206 }
81 207
82 void Retriever::OnDownloaded(bool success, 208 void Retriever::OnDownloaded(bool success,
83 const std::string& url, 209 const std::string& url,
84 const std::string& downloaded_data) { 210 const std::string& downloaded_data) {
85 const std::string& key = GetKeyForUrl(url); 211 const std::string& key = GetKeyForUrl(url);
86 std::string response; 212 std::map<std::string, std::string>::iterator stale_data_it =
213 stale_data_.find(key);
214
87 if (success) { 215 if (success) {
88 storage_->Put(key, downloaded_data); 216 storage_->Put(key, PrependTimestamp(downloaded_data));
89 response = downloaded_data; 217 InvokeCallbackForKey(key, success, downloaded_data);
218 } else if (stale_data_it != stale_data_.end()) {
219 InvokeCallbackForKey(key, true, stale_data_it->second);
90 } else { 220 } else {
91 success = FallbackDataStore::Get(key, &response); 221 std::string fallback;
222 success = FallbackDataStore::Get(key, &fallback);
223 InvokeCallbackForKey(key, success, fallback);
92 } 224 }
93 225
94 scoped_ptr<Callback> retrieved = GetCallbackForKey(key); 226 if (stale_data_it != stale_data_.end()) {
95 if (retrieved != NULL) { 227 stale_data_.erase(stale_data_it);
96 (*retrieved)(success, key, response);
97 } 228 }
98 } 229 }
99 230
100 std::string Retriever::GetUrlForKey(const std::string& key) const { 231 std::string Retriever::GetUrlForKey(const std::string& key) const {
101 return validation_data_url_ + key; 232 return validation_data_url_ + key;
102 } 233 }
103 234
104 std::string Retriever::GetKeyForUrl(const std::string& url) const { 235 std::string Retriever::GetKeyForUrl(const std::string& url) const {
105 if (url.compare(0, validation_data_url_.length(), validation_data_url_) == 0) 236 return
106 return url.substr(validation_data_url_.length()); 237 url.compare(0, validation_data_url_.length(), validation_data_url_) == 0
107 238 ? url.substr(validation_data_url_.length())
108 return std::string(); 239 : std::string();
109 } 240 }
110 241
111 bool Retriever::IsValidationDataUrl(const std::string& url) const { 242 bool Retriever::IsValidationDataUrl(const std::string& url) const {
112 return 243 return
113 url.compare(0, validation_data_url_.length(), validation_data_url_) == 0; 244 url.compare(0, validation_data_url_.length(), validation_data_url_) == 0;
114 } 245 }
115 246
116 scoped_ptr<Retriever::Callback> Retriever::GetCallbackForKey( 247 void Retriever::InvokeCallbackForKey(const std::string& key,
117 const std::string& key) { 248 bool success,
249 const std::string& data) {
118 std::map<std::string, Callback*>::iterator iter = 250 std::map<std::string, Callback*>::iterator iter =
119 requests_.find(key); 251 requests_.find(key);
120 if (iter == requests_.end()) { 252 if (iter == requests_.end()) {
121 // An abandonened request. 253 // An abandoned request.
122 return scoped_ptr<Callback>(); 254 return;
123 } 255 }
124 scoped_ptr<Callback> callback(iter->second); 256 scoped_ptr<Callback> callback(iter->second);
125 requests_.erase(iter); 257 requests_.erase(iter);
126 return callback.Pass(); 258 if (callback == NULL) {
259 return;
260 }
261 (*callback)(success, key, data);
127 } 262 }
128 263
129 } // namespace addressinput 264 } // namespace addressinput
130 } // namespace i18n 265 } // namespace i18n
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698