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

Side by Side Diff: net/http/http_cache.cc

Issue 455623003: stale-while-revalidate experimental implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Stop setting Cache-Control: max-age=0 on async revalidations. Created 6 years, 3 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 "net/http/http_cache.h" 5 #include "net/http/http_cache.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/compiler_specific.h" 9 #include "base/compiler_specific.h"
10 10
11 #if defined(OS_POSIX) 11 #if defined(OS_POSIX)
12 #include <unistd.h> 12 #include <unistd.h>
13 #endif 13 #endif
14 14
15 #include "base/bind.h" 15 #include "base/bind.h"
16 #include "base/bind_helpers.h" 16 #include "base/bind_helpers.h"
17 #include "base/callback.h" 17 #include "base/callback.h"
18 #include "base/file_util.h" 18 #include "base/file_util.h"
19 #include "base/format_macros.h" 19 #include "base/format_macros.h"
20 #include "base/location.h" 20 #include "base/location.h"
21 #include "base/memory/ref_counted.h" 21 #include "base/memory/ref_counted.h"
22 #include "base/message_loop/message_loop.h" 22 #include "base/message_loop/message_loop.h"
23 #include "base/metrics/field_trial.h" 23 #include "base/metrics/field_trial.h"
24 #include "base/metrics/histogram.h"
24 #include "base/pickle.h" 25 #include "base/pickle.h"
25 #include "base/stl_util.h" 26 #include "base/stl_util.h"
26 #include "base/strings/string_number_conversions.h" 27 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_util.h" 28 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h" 29 #include "base/strings/stringprintf.h"
29 #include "base/threading/worker_pool.h" 30 #include "base/threading/worker_pool.h"
31 #include "base/time/time.h"
30 #include "net/base/cache_type.h" 32 #include "net/base/cache_type.h"
31 #include "net/base/io_buffer.h" 33 #include "net/base/io_buffer.h"
32 #include "net/base/load_flags.h" 34 #include "net/base/load_flags.h"
33 #include "net/base/net_errors.h" 35 #include "net/base/net_errors.h"
36 #include "net/base/network_delegate.h"
34 #include "net/base/upload_data_stream.h" 37 #include "net/base/upload_data_stream.h"
35 #include "net/disk_cache/disk_cache.h" 38 #include "net/disk_cache/disk_cache.h"
36 #include "net/http/disk_based_cert_cache.h" 39 #include "net/http/disk_based_cert_cache.h"
37 #include "net/http/disk_cache_based_quic_server_info.h" 40 #include "net/http/disk_cache_based_quic_server_info.h"
38 #include "net/http/http_cache_transaction.h" 41 #include "net/http/http_cache_transaction.h"
39 #include "net/http/http_network_layer.h" 42 #include "net/http/http_network_layer.h"
40 #include "net/http/http_network_session.h" 43 #include "net/http/http_network_session.h"
41 #include "net/http/http_request_info.h" 44 #include "net/http/http_request_info.h"
42 #include "net/http/http_response_headers.h" 45 #include "net/http/http_response_headers.h"
43 #include "net/http/http_response_info.h" 46 #include "net/http/http_response_info.h"
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 virtual QuicServerInfo* GetForServer( 288 virtual QuicServerInfo* GetForServer(
286 const QuicServerId& server_id) OVERRIDE { 289 const QuicServerId& server_id) OVERRIDE {
287 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_); 290 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
288 } 291 }
289 292
290 private: 293 private:
291 HttpCache* const http_cache_; 294 HttpCache* const http_cache_;
292 }; 295 };
293 296
294 //----------------------------------------------------------------------------- 297 //-----------------------------------------------------------------------------
298
299 class HttpCache::AsyncValidation {
300 public:
301 AsyncValidation(const HttpRequestInfo& original_request, HttpCache* cache)
302 : request_(original_request), cache_(cache) {}
303 ~AsyncValidation() {}
304
305 void Start(const BoundNetLog& net_log, NetworkDelegate* network_delegate);
306
307 private:
308 void OnStarted(int result);
309 void DoRead();
310 void OnRead(int result);
311
312 // Terminate this request with net error code |result|. Logs the transaction
313 // result and asks HttpCache to delete this object.
314 // If there was a client or server certificate error, it cannot be recovered
315 // asynchronously, so we need to prevent futures attempts to asynchronously
316 // fetch the resource. In this case, the cache entry is doomed.
317 void Terminate(int result);
318
319 HttpRequestInfo request_;
320 scoped_refptr<IOBuffer> buf_;
321 CompletionCallback read_callback_;
322 scoped_ptr<Transaction> transaction_;
323 base::Time start_time_;
324
325 // The HttpCache object owns this object. This object is always deleted before
326 // the pointer to the cache becomes invalid.
327 HttpCache* cache_;
328
329 DISALLOW_COPY_AND_ASSIGN(AsyncValidation);
330 };
331
332 void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log,
333 NetworkDelegate* network_delegate) {
334 transaction_.reset(new Transaction(IDLE, cache_));
rvargas (doing something else) 2014/08/29 23:36:06 It's better to keep transaction creation centraliz
Adam Rice 2014/09/01 14:42:59 Done.
335 if (network_delegate) {
336 // This code is necessary to enable async transactions to pass over the
337 // data-reduction proxy. This is a violation of the "once-and-only-once"
338 // principle, since it copies code from URLRequestHttpJob. We cannot use the
339 // original callback passed to HttpCache::Transaction by URLRequestHttpJob
340 // as it will only be valid as long as the URLRequestHttpJob object is
341 // alive, and that object will be deleted as soon as the synchronous request
342 // completes.
343 //
344 // This code is also an encapsulation violation. We are exploiting the fact
345 // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never
346 // actually used for anything, and so can be NULL.
347 //
348 // TODO(ricea): Do this better.
349 transaction_->SetBeforeProxyHeadersSentCallback(
350 base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders,
351 base::Unretained(network_delegate),
352 static_cast<URLRequest*>(NULL)));
353 // The above use of base::Unretained is safe because the NetworkDelegate has
354 // to live at least as long as the HttpNetworkSession which has to live as
355 // least as long as the HttpNetworkLayer which has to live at least as long
356 // this HttpCache object.
357 }
358
359 transaction_->ValidateInternalCache();
rvargas (doing something else) 2014/08/29 23:36:06 This looks out of the blue I just found the menti
Adam Rice 2014/09/01 14:43:00 I originally was using load flags, but it made me
rvargas (doing something else) 2014/09/03 01:25:53 I think it is interesting to bring this up. Basica
Adam Rice 2014/09/05 15:17:11 Okay, I have added LOAD_ASYNC_REVALIDATION and rem
360 start_time_ = base::Time::Now();
361 // This use of base::Unretained is safe because |transaction_| is owned by
362 // this object.
363 read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this));
364 // This use of base::Unretained is safe as above.
365 int rv = transaction_->Start(
366 &request_,
367 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)),
368 net_log);
369
370 if (rv == ERR_IO_PENDING)
371 return;
372
373 OnStarted(rv);
374 }
375
376 void HttpCache::AsyncValidation::OnStarted(int result) {
377 if (result != OK) {
378 DVLOG(1) << "Asynchronous transaction start failed for " << request_.url;
379 Terminate(result);
380 return;
381 }
382
383 DoRead();
384 }
385
386 void HttpCache::AsyncValidation::DoRead() {
387 const size_t kBufSize = 4096;
388 if (!buf_)
389 buf_ = new IOBuffer(kBufSize);
390
391 int rv = 0;
392 do {
393 rv = transaction_->Read(buf_.get(), kBufSize, read_callback_);
394 } while (rv > 0);
395
396 if (rv == ERR_IO_PENDING)
397 return;
398
399 OnRead(rv);
400 }
401
402 void HttpCache::AsyncValidation::OnRead(int result) {
403 if (result > 0) {
404 DoRead();
405 return;
406 }
407 Terminate(result);
408 }
409
410 void HttpCache::AsyncValidation::Terminate(int result) {
411 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result))
412 cache_->DoomEntry(transaction_->key(), transaction_.get());
rvargas (doing something else) 2014/08/29 23:36:06 why? Looks like a problem with this code, not with
Adam Rice 2014/09/01 14:43:00 Consider the case where the remote server suddenly
rvargas (doing something else) 2014/09/03 01:25:53 We don't have to pop up an auth dialog for every r
Adam Rice 2014/09/05 15:17:11 For the final design, I'm thinking of something li
rvargas (doing something else) 2014/09/05 22:52:39 My suggestion for the final design would be that t
Adam Rice 2014/09/09 12:36:45 The fact that another asynchronous validation shou
413 base::TimeDelta time_saved = base::Time::Now() - start_time_;
414 UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationTimeSaved", time_saved);
rvargas (doing something else) 2014/08/29 23:36:06 What is this measuring? This cannot be accounted a
Adam Rice 2014/09/01 14:43:00 Sadly, no. I was thinking of splitting it into tw
rvargas (doing something else) 2014/09/03 01:25:53 I'm fine with it as long as we don't use as a metr
Adam Rice 2014/09/05 15:17:11 I renamed it AsyncValidationDuration to avoid misu
415 transaction_->net_log().EndEventWithNetErrorCode(
416 NetLog::TYPE_ASYNC_REVALIDATION, result);
417 cache_->DeleteAsyncValidation(this);
418 // |this| is deleted.
419 }
420
421 //-----------------------------------------------------------------------------
295 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params, 422 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
296 BackendFactory* backend_factory) 423 BackendFactory* backend_factory)
297 : net_log_(params.net_log), 424 : net_log_(params.net_log),
298 backend_factory_(backend_factory), 425 backend_factory_(backend_factory),
299 building_backend_(false), 426 building_backend_(false),
300 bypass_lock_for_test_(false), 427 bypass_lock_for_test_(false),
428 use_stale_while_revalidate_(params.use_stale_while_revalidate),
301 mode_(NORMAL), 429 mode_(NORMAL),
302 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))), 430 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
303 weak_factory_(this) { 431 weak_factory_(this) {
304 SetupQuicServerInfoFactory(network_layer_->GetSession()); 432 SetupQuicServerInfoFactory(network_layer_->GetSession());
305 } 433 }
306 434
307 435
308 // This call doesn't change the shared |session|'s QuicServerInfoFactory because 436 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
309 // |session| is shared. 437 // |session| is shared.
310 HttpCache::HttpCache(HttpNetworkSession* session, 438 HttpCache::HttpCache(HttpNetworkSession* session,
311 BackendFactory* backend_factory) 439 BackendFactory* backend_factory)
312 : net_log_(session->net_log()), 440 : net_log_(session->net_log()),
313 backend_factory_(backend_factory), 441 backend_factory_(backend_factory),
314 building_backend_(false), 442 building_backend_(false),
315 bypass_lock_for_test_(false), 443 bypass_lock_for_test_(false),
444 use_stale_while_revalidate_(session->params().use_stale_while_revalidate),
316 mode_(NORMAL), 445 mode_(NORMAL),
317 network_layer_(new HttpNetworkLayer(session)), 446 network_layer_(new HttpNetworkLayer(session)),
318 weak_factory_(this) { 447 weak_factory_(this) {
319 } 448 }
320 449
321 HttpCache::HttpCache(HttpTransactionFactory* network_layer, 450 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
322 NetLog* net_log, 451 NetLog* net_log,
323 BackendFactory* backend_factory) 452 BackendFactory* backend_factory)
324 : net_log_(net_log), 453 : net_log_(net_log),
325 backend_factory_(backend_factory), 454 backend_factory_(backend_factory),
326 building_backend_(false), 455 building_backend_(false),
327 bypass_lock_for_test_(false), 456 bypass_lock_for_test_(false),
457 use_stale_while_revalidate_(false),
328 mode_(NORMAL), 458 mode_(NORMAL),
329 network_layer_(network_layer), 459 network_layer_(network_layer),
330 weak_factory_(this) { 460 weak_factory_(this) {
331 SetupQuicServerInfoFactory(network_layer_->GetSession()); 461 SetupQuicServerInfoFactory(network_layer_->GetSession());
462 HttpNetworkSession* session = network_layer_->GetSession();
463 if (session)
464 use_stale_while_revalidate_ = session->params().use_stale_while_revalidate;
332 } 465 }
333 466
334 HttpCache::~HttpCache() { 467 HttpCache::~HttpCache() {
335 // Transactions should see an invalid cache after this point; otherwise they 468 // Transactions should see an invalid cache after this point; otherwise they
336 // could see an inconsistent object (half destroyed). 469 // could see an inconsistent object (half destroyed).
337 weak_factory_.InvalidateWeakPtrs(); 470 weak_factory_.InvalidateWeakPtrs();
338 471
339 // If we have any active entries remaining, then we need to deactivate them. 472 // If we have any active entries remaining, then we need to deactivate them.
340 // We may have some pending calls to OnProcessPendingQueue, but since those 473 // We may have some pending calls to OnProcessPendingQueue, but since those
341 // won't run (due to our destruction), we can simply ignore the corresponding 474 // won't run (due to our destruction), we can simply ignore the corresponding
342 // will_process_pending_queue flag. 475 // will_process_pending_queue flag.
343 while (!active_entries_.empty()) { 476 while (!active_entries_.empty()) {
344 ActiveEntry* entry = active_entries_.begin()->second; 477 ActiveEntry* entry = active_entries_.begin()->second;
345 entry->will_process_pending_queue = false; 478 entry->will_process_pending_queue = false;
346 entry->pending_queue.clear(); 479 entry->pending_queue.clear();
347 entry->readers.clear(); 480 entry->readers.clear();
348 entry->writer = NULL; 481 entry->writer = NULL;
349 DeactivateEntry(entry); 482 DeactivateEntry(entry);
350 } 483 }
351 484
352 STLDeleteElements(&doomed_entries_); 485 STLDeleteElements(&doomed_entries_);
486 STLDeleteElements(&async_validations_);
353 487
354 // Before deleting pending_ops_, we have to make sure that the disk cache is 488 // Before deleting pending_ops_, we have to make sure that the disk cache is
355 // done with said operations, or it will attempt to use deleted data. 489 // done with said operations, or it will attempt to use deleted data.
356 cert_cache_.reset(); 490 cert_cache_.reset();
357 disk_cache_.reset(); 491 disk_cache_.reset();
358 492
359 PendingOpsMap::iterator pending_it = pending_ops_.begin(); 493 PendingOpsMap::iterator pending_it = pending_ops_.begin();
360 for (; pending_it != pending_ops_.end(); ++pending_it) { 494 for (; pending_it != pending_ops_.end(); ++pending_it) {
361 // We are not notifying the transactions about the cache going away, even 495 // We are not notifying the transactions about the cache going away, even
362 // though they are waiting for a callback that will never fire. 496 // though they are waiting for a callback that will never fire.
(...skipping 668 matching lines...) Expand 10 before | Expand all | Expand 10 after
1031 // not delete the entry before OnProcessPendingQueue runs. 1165 // not delete the entry before OnProcessPendingQueue runs.
1032 if (entry->will_process_pending_queue) 1166 if (entry->will_process_pending_queue)
1033 return; 1167 return;
1034 entry->will_process_pending_queue = true; 1168 entry->will_process_pending_queue = true;
1035 1169
1036 base::MessageLoop::current()->PostTask( 1170 base::MessageLoop::current()->PostTask(
1037 FROM_HERE, 1171 FROM_HERE,
1038 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry)); 1172 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1039 } 1173 }
1040 1174
1175 void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request,
1176 const BoundNetLog& net_log) {
1177 DCHECK(use_stale_while_revalidate_);
1178 AsyncValidation* job = new AsyncValidation(original_request, this);
1179 bool insert_ok = async_validations_.insert(job).second;
1180 DCHECK(insert_ok);
rvargas (doing something else) 2014/08/29 23:36:06 why can't this fail?
Adam Rice 2014/09/01 14:43:00 It is matched with the erase() in DeleteAsyncValid
rvargas (doing something else) 2014/09/03 01:25:52 But we should not be performing two async validati
Adam Rice 2014/09/05 15:17:11 It might be possible to get multiple AsyncValidati
rvargas (doing something else) 2014/09/05 22:52:39 Sorry I was not clear. Yes, things will be queued
Adam Rice 2014/09/09 12:36:45 Done.
1181 HttpNetworkSession* network_session = GetSession();
1182 NetworkDelegate* network_delegate = NULL;
1183 if (network_session)
1184 network_delegate = network_session->network_delegate();
1185 job->Start(net_log, network_delegate);
rvargas (doing something else) 2014/08/29 23:36:06 AsyncValidation is not a UrlRequest::Job so it's b
Adam Rice 2014/09/01 14:43:00 Done.
1186 // |job| may have been deleted here.
1187 }
1188
1189 void HttpCache::DeleteAsyncValidation(AsyncValidation* async_validation) {
1190 size_t erased = async_validations_.erase(async_validation);
1191 DCHECK_EQ(1U, erased);
1192 delete async_validation;
1193 }
1194
1041 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { 1195 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1042 entry->will_process_pending_queue = false; 1196 entry->will_process_pending_queue = false;
1043 DCHECK(!entry->writer); 1197 DCHECK(!entry->writer);
1044 1198
1045 // If no one is interested in this entry, then we can deactivate it. 1199 // If no one is interested in this entry, then we can deactivate it.
1046 if (entry->pending_queue.empty()) { 1200 if (entry->pending_queue.empty()) {
1047 if (entry->readers.empty()) 1201 if (entry->readers.empty())
1048 DestroyEntry(entry); 1202 DestroyEntry(entry);
1049 return; 1203 return;
1050 } 1204 }
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
1201 building_backend_ = false; 1355 building_backend_ = false;
1202 DeletePendingOp(pending_op); 1356 DeletePendingOp(pending_op);
1203 } 1357 }
1204 1358
1205 // The cache may be gone when we return from the callback. 1359 // The cache may be gone when we return from the callback.
1206 if (!item->DoCallback(result, disk_cache_.get())) 1360 if (!item->DoCallback(result, disk_cache_.get()))
1207 item->NotifyTransaction(result, NULL); 1361 item->NotifyTransaction(result, NULL);
1208 } 1362 }
1209 1363
1210 } // namespace net 1364 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698