OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "services/url_response_disk_cache/url_response_disk_cache_impl.h" | 5 #include "services/url_response_disk_cache/url_response_disk_cache_impl.h" |
6 | 6 |
7 #include <dirent.h> | 7 #include <dirent.h> |
8 #include <fcntl.h> | 8 #include <fcntl.h> |
9 #include <sys/types.h> | 9 #include <sys/types.h> |
10 | 10 |
(...skipping 17 matching lines...) Expand all Loading... |
28 #include "services/url_response_disk_cache/url_response_disk_cache_entry.mojom.h
" | 28 #include "services/url_response_disk_cache/url_response_disk_cache_entry.mojom.h
" |
29 #include "third_party/zlib/google/zip_reader.h" | 29 #include "third_party/zlib/google/zip_reader.h" |
30 #include "url/gurl.h" | 30 #include "url/gurl.h" |
31 | 31 |
32 namespace mojo { | 32 namespace mojo { |
33 | 33 |
34 namespace { | 34 namespace { |
35 | 35 |
36 // The current version of the cache. This should only be incremented. When this | 36 // The current version of the cache. This should only be incremented. When this |
37 // is incremented, all current cache entries will be invalidated. | 37 // is incremented, all current cache entries will be invalidated. |
38 const uint32_t kCurrentVersion = 0; | 38 const uint32_t kCurrentVersion = 1; |
39 | 39 |
40 // The delay to wait before starting deleting data. This is delayed to not | 40 // The delay to wait before starting deleting data. This is delayed to not |
41 // interfere with the shell startup. | 41 // interfere with the shell startup. |
42 const uint32_t kTrashDelayInSeconds = 60; | 42 const uint32_t kTrashDelayInSeconds = 60; |
43 | 43 |
| 44 // The delay between the time an entry is invalidated and the cache not |
| 45 // returning it anymore. |
| 46 const uint32_t kTimeUntilInvalidationInSeconds = 90; |
| 47 |
44 const char kEtagHeader[] = "etag"; | 48 const char kEtagHeader[] = "etag"; |
45 | 49 |
46 // Create a new identifier for a cache entry. This will be used as an unique | 50 // Create a new identifier for a cache entry. This will be used as an unique |
47 // directory name. | 51 // directory name. |
48 std::string GetNewIdentifier() { | 52 std::string GetNewIdentifier() { |
49 char bytes[32]; | 53 char bytes[32]; |
50 crypto::RandBytes(bytes, arraysize(bytes)); | 54 crypto::RandBytes(bytes, arraysize(bytes)); |
51 return base::HexEncode(bytes, arraysize(bytes)); | 55 return base::HexEncode(bytes, arraysize(bytes)); |
52 } | 56 } |
53 | 57 |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 GetStagingDirectory().Append(identifier); | 172 GetStagingDirectory().Append(identifier); |
169 base::FilePath entry_directory = GetCacheDirectory().Append(identifier); | 173 base::FilePath entry_directory = GetCacheDirectory().Append(identifier); |
170 base::FilePath response_body_path = entry_directory.Append(identifier); | 174 base::FilePath response_body_path = entry_directory.Append(identifier); |
171 base::FilePath consumer_cache_directory = | 175 base::FilePath consumer_cache_directory = |
172 GetConsumerCacheDirectory(entry_directory); | 176 GetConsumerCacheDirectory(entry_directory); |
173 | 177 |
174 CacheEntryPtr entry = CacheEntry::New(); | 178 CacheEntryPtr entry = CacheEntry::New(); |
175 entry->response = response.Pass(); | 179 entry->response = response.Pass(); |
176 entry->entry_directory = entry_directory.value(); | 180 entry->entry_directory = entry_directory.value(); |
177 entry->response_body_path = response_body_path.value(); | 181 entry->response_body_path = response_body_path.value(); |
| 182 entry->last_invalidation = base::Time::Max().ToInternalValue(); |
178 | 183 |
179 db->PutNew(request_origin, url, entry.Pass()); | 184 db->PutNew(request_origin, url, entry.Pass()); |
180 | 185 |
181 if (!base::CreateDirectoryAndGetError(entry_directory, nullptr) || | 186 if (!base::CreateDirectoryAndGetError(entry_directory, nullptr) || |
182 !base::CreateDirectoryAndGetError(consumer_cache_directory, nullptr) || | 187 !base::CreateDirectoryAndGetError(consumer_cache_directory, nullptr) || |
183 !base::ReplaceFile(staged_response_body_path, response_body_path, | 188 !base::ReplaceFile(staged_response_body_path, response_body_path, |
184 nullptr)) { | 189 nullptr)) { |
185 MovePathIntoDir(entry_directory, GetStagingDirectory()); | 190 MovePathIntoDir(entry_directory, GetStagingDirectory()); |
186 callback.Run(base::FilePath(), base::FilePath()); | 191 callback.Run(base::FilePath(), base::FilePath()); |
187 return; | 192 return; |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 return false; | 242 return false; |
238 std::vector<std::string> response_etags = | 243 std::vector<std::string> response_etags = |
239 GetHeaderValues(etag_header_name, response->headers); | 244 GetHeaderValues(etag_header_name, response->headers); |
240 if (response_etags.size() == 0) | 245 if (response_etags.size() == 0) |
241 return false; | 246 return false; |
242 | 247 |
243 // Looking for the first etag header. | 248 // Looking for the first etag header. |
244 return entry_etags[0] == response_etags[0]; | 249 return entry_etags[0] == response_etags[0]; |
245 } | 250 } |
246 | 251 |
| 252 void UpdateLastInvalidation(scoped_refptr<URLResponseDiskCacheDB> db, |
| 253 CacheKeyPtr key, |
| 254 const base::Time& time) { |
| 255 CacheEntryPtr entry = db->Get(key.Clone()); |
| 256 entry->last_invalidation = time.ToInternalValue(); |
| 257 db->Put(key.Pass(), entry.Pass()); |
| 258 } |
| 259 |
247 void PruneCache(scoped_refptr<URLResponseDiskCacheDB> db, | 260 void PruneCache(scoped_refptr<URLResponseDiskCacheDB> db, |
248 const scoped_ptr<URLResponseDiskCacheDB::Iterator>& iterator) { | 261 const scoped_ptr<URLResponseDiskCacheDB::Iterator>& iterator) { |
249 CacheKeyPtr last_key; | 262 CacheKeyPtr last_key; |
250 CacheKeyPtr key; | 263 CacheKeyPtr key; |
251 CacheEntryPtr entry; | 264 CacheEntryPtr entry; |
252 while (iterator->HasNext()) { | 265 while (iterator->HasNext()) { |
253 iterator->GetNext(&key, &entry); | 266 iterator->GetNext(&key, &entry); |
254 if (last_key && last_key->request_origin == key->request_origin && | 267 if (last_key && last_key->request_origin == key->request_origin && |
255 last_key->url == key->url) { | 268 last_key->url == key->url) { |
256 base::FilePath entry_directory = base::FilePath(entry->entry_directory); | 269 base::FilePath entry_directory = base::FilePath(entry->entry_directory); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 scoped_refptr<URLResponseDiskCacheDB> db, | 323 scoped_refptr<URLResponseDiskCacheDB> db, |
311 const std::string& remote_application_url, | 324 const std::string& remote_application_url, |
312 InterfaceRequest<URLResponseDiskCache> request) | 325 InterfaceRequest<URLResponseDiskCache> request) |
313 : task_runner_(task_runner), db_(db), binding_(this, request.Pass()) { | 326 : task_runner_(task_runner), db_(db), binding_(this, request.Pass()) { |
314 request_origin_ = GURL(remote_application_url).GetOrigin().spec(); | 327 request_origin_ = GURL(remote_application_url).GetOrigin().spec(); |
315 } | 328 } |
316 | 329 |
317 URLResponseDiskCacheImpl::~URLResponseDiskCacheImpl() { | 330 URLResponseDiskCacheImpl::~URLResponseDiskCacheImpl() { |
318 } | 331 } |
319 | 332 |
| 333 bool IsInvalidated(const CacheEntryPtr& entry) { |
| 334 if (!entry) |
| 335 return true; |
| 336 return base::Time::Now() - |
| 337 base::Time::FromInternalValue(entry->last_invalidation) > |
| 338 base::TimeDelta::FromSeconds(kTimeUntilInvalidationInSeconds); |
| 339 } |
| 340 |
320 void URLResponseDiskCacheImpl::Get(const String& url, | 341 void URLResponseDiskCacheImpl::Get(const String& url, |
321 const GetCallback& callback) { | 342 const GetCallback& callback) { |
322 CacheEntryPtr entry = db_->GetNewest(request_origin_, CanonicalizeURL(url)); | 343 CacheKeyPtr key; |
323 if (!IsCacheEntryValid(entry)) { | 344 CacheEntryPtr entry = |
| 345 db_->GetNewest(request_origin_, CanonicalizeURL(url), &key); |
| 346 if (IsInvalidated(entry) || !IsCacheEntryValid(entry)) { |
324 callback.Run(URLResponsePtr(), Array<uint8_t>(), Array<uint8_t>()); | 347 callback.Run(URLResponsePtr(), Array<uint8_t>(), Array<uint8_t>()); |
325 return; | 348 return; |
326 } | 349 } |
327 callback.Run(entry->response.Pass(), | 350 callback.Run(entry->response.Pass(), |
328 PathToArray(base::FilePath(entry->response_body_path)), | 351 PathToArray(base::FilePath(entry->response_body_path)), |
329 PathToArray(GetConsumerCacheDirectory( | 352 PathToArray(GetConsumerCacheDirectory( |
330 base::FilePath(entry->entry_directory)))); | 353 base::FilePath(entry->entry_directory)))); |
| 354 UpdateLastInvalidation(db_, key.Pass(), base::Time::Now()); |
| 355 } |
| 356 |
| 357 void URLResponseDiskCacheImpl::Validate(const String& url) { |
| 358 CacheKeyPtr key; |
| 359 CacheEntryPtr entry = |
| 360 db_->GetNewest(request_origin_, CanonicalizeURL(url), &key); |
| 361 if (entry) |
| 362 UpdateLastInvalidation(db_, key.Pass(), base::Time::Max()); |
331 } | 363 } |
332 | 364 |
333 void URLResponseDiskCacheImpl::Update(URLResponsePtr response) { | 365 void URLResponseDiskCacheImpl::Update(URLResponsePtr response) { |
334 UpdateAndGetInternal(response.Pass(), base::Bind(&DoNothing)); | 366 UpdateAndGetInternal(response.Pass(), base::Bind(&DoNothing)); |
335 } | 367 } |
336 | 368 |
337 void URLResponseDiskCacheImpl::UpdateAndGet( | 369 void URLResponseDiskCacheImpl::UpdateAndGet( |
338 URLResponsePtr response, | 370 URLResponsePtr response, |
339 const UpdateAndGetCallback& callback) { | 371 const UpdateAndGetCallback& callback) { |
340 UpdateAndGetInternal(response.Pass(), base::Bind(&RunMojoCallback, callback)); | 372 UpdateAndGetInternal(response.Pass(), base::Bind(&RunMojoCallback, callback)); |
341 } | 373 } |
342 | 374 |
343 void URLResponseDiskCacheImpl::UpdateAndGetExtracted( | 375 void URLResponseDiskCacheImpl::UpdateAndGetExtracted( |
344 URLResponsePtr response, | 376 URLResponsePtr response, |
345 const UpdateAndGetExtractedCallback& callback) { | 377 const UpdateAndGetExtractedCallback& callback) { |
346 if (response->error || | 378 UpdateAndGetInternal( |
347 (response->status_code >= 400 && response->status_code < 600)) { | 379 response.Pass(), |
348 callback.Run(Array<uint8_t>(), Array<uint8_t>()); | 380 base::Bind(&URLResponseDiskCacheImpl::UpdateAndGetExtractedInternal, |
349 return; | 381 base::Unretained(this), |
350 } | 382 base::Bind(&RunMojoCallback, callback))); |
351 | |
352 std::string url = CanonicalizeURL(response->url); | |
353 | |
354 // Check if the response is cached and valid. If that's the case, returns the | |
355 // cached value. | |
356 CacheEntryPtr entry = db_->GetNewest(request_origin_, url); | |
357 | |
358 if (!IsCacheEntryFresh(response, entry)) { | |
359 UpdateAndGetInternal( | |
360 response.Pass(), | |
361 base::Bind(&URLResponseDiskCacheImpl::UpdateAndGetExtractedInternal, | |
362 base::Unretained(this), | |
363 base::Bind(&RunMojoCallback, callback))); | |
364 return; | |
365 } | |
366 | |
367 base::FilePath entry_directory = base::FilePath(entry->entry_directory); | |
368 base::FilePath extraction_directory = GetExtractionDirectory(entry_directory); | |
369 if (!PathExists(GetExtractionSentinel(entry_directory))) { | |
370 UpdateAndGetExtractedInternal(base::Bind(&RunMojoCallback, callback), | |
371 base::FilePath(entry->response_body_path), | |
372 GetConsumerCacheDirectory(entry_directory)); | |
373 return; | |
374 } | |
375 | |
376 callback.Run(PathToArray(extraction_directory), | |
377 PathToArray(GetConsumerCacheDirectory(entry_directory))); | |
378 } | 383 } |
379 | 384 |
380 void URLResponseDiskCacheImpl::UpdateAndGetInternal( | 385 void URLResponseDiskCacheImpl::UpdateAndGetInternal( |
381 URLResponsePtr response, | 386 URLResponsePtr response, |
382 const ResponseFileAndCacheDirCallback& callback) { | 387 const ResponseFileAndCacheDirCallback& callback) { |
383 if (response->error || | 388 if (response->error || |
384 (response->status_code >= 400 && response->status_code < 600)) { | 389 (response->status_code >= 400 && response->status_code < 600)) { |
385 callback.Run(base::FilePath(), base::FilePath()); | 390 callback.Run(base::FilePath(), base::FilePath()); |
386 return; | 391 return; |
387 } | 392 } |
388 | 393 |
389 std::string url = CanonicalizeURL(response->url); | 394 std::string url = CanonicalizeURL(response->url); |
390 | 395 |
391 // Check if the response is cached and valid. If that's the case, returns | 396 // Check if the response is cached and valid. If that's the case, returns |
392 // the cached value. | 397 // the cached value. |
393 CacheEntryPtr entry = db_->GetNewest(request_origin_, url); | 398 CacheKeyPtr key; |
| 399 CacheEntryPtr entry = db_->GetNewest(request_origin_, url, &key); |
394 if (IsCacheEntryFresh(response, entry)) { | 400 if (IsCacheEntryFresh(response, entry)) { |
395 callback.Run( | 401 callback.Run( |
396 base::FilePath(entry->response_body_path), | 402 base::FilePath(entry->response_body_path), |
397 GetConsumerCacheDirectory(base::FilePath(entry->entry_directory))); | 403 GetConsumerCacheDirectory(base::FilePath(entry->entry_directory))); |
| 404 UpdateLastInvalidation(db_, key.Pass(), base::Time::Max()); |
398 return; | 405 return; |
399 } | 406 } |
400 | 407 |
401 if (!response->body.is_valid()) { | 408 if (!response->body.is_valid()) { |
402 callback.Run(base::FilePath(), base::FilePath()); | 409 callback.Run(base::FilePath(), base::FilePath()); |
403 return; | 410 return; |
404 } | 411 } |
405 | 412 |
406 std::string identifier = GetNewIdentifier(); | 413 std::string identifier = GetNewIdentifier(); |
407 // The content is copied to the staging directory so that files are not leaked | 414 // The content is copied to the staging directory so that files are not leaked |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 return; | 472 return; |
466 } | 473 } |
467 } | 474 } |
468 // We can ignore write error, as it will just force to clear the cache on the | 475 // We can ignore write error, as it will just force to clear the cache on the |
469 // next request. | 476 // next request. |
470 WriteFile(GetExtractionSentinel(entry_directory), nullptr, 0); | 477 WriteFile(GetExtractionSentinel(entry_directory), nullptr, 0); |
471 callback.Run(extraction_directory, consumer_cache_directory); | 478 callback.Run(extraction_directory, consumer_cache_directory); |
472 } | 479 } |
473 | 480 |
474 } // namespace mojo | 481 } // namespace mojo |
OLD | NEW |