OLD | NEW |
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/files/file_util.h" | 18 #include "base/files/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 Loading... |
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, |
| 306 scoped_ptr<Transaction> transaction, |
| 307 NetworkDelegate* network_delegate); |
| 308 |
| 309 private: |
| 310 void OnStarted(int result); |
| 311 void DoRead(); |
| 312 void OnRead(int result); |
| 313 |
| 314 // Terminate this request with net error code |result|. Logs the transaction |
| 315 // result and asks HttpCache to delete this object. |
| 316 // If there was a client or server certificate error, it cannot be recovered |
| 317 // asynchronously, so we need to prevent future attempts to asynchronously |
| 318 // fetch the resource. In this case, the cache entry is doomed. |
| 319 void Terminate(int result); |
| 320 |
| 321 HttpRequestInfo request_; |
| 322 scoped_refptr<IOBuffer> buf_; |
| 323 CompletionCallback read_callback_; |
| 324 scoped_ptr<Transaction> transaction_; |
| 325 base::Time start_time_; |
| 326 |
| 327 // The HttpCache object owns this object. This object is always deleted before |
| 328 // the pointer to the cache becomes invalid. |
| 329 HttpCache* cache_; |
| 330 |
| 331 DISALLOW_COPY_AND_ASSIGN(AsyncValidation); |
| 332 }; |
| 333 |
| 334 void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log, |
| 335 scoped_ptr<Transaction> transaction, |
| 336 NetworkDelegate* network_delegate) { |
| 337 transaction_ = transaction.Pass(); |
| 338 if (network_delegate) { |
| 339 // This code is necessary to enable async transactions to pass over the |
| 340 // data-reduction proxy. This is a violation of the "once-and-only-once" |
| 341 // principle, since it copies code from URLRequestHttpJob. We cannot use the |
| 342 // original callback passed to HttpCache::Transaction by URLRequestHttpJob |
| 343 // as it will only be valid as long as the URLRequestHttpJob object is |
| 344 // alive, and that object will be deleted as soon as the synchronous request |
| 345 // completes. |
| 346 // |
| 347 // This code is also an encapsulation violation. We are exploiting the fact |
| 348 // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never |
| 349 // actually used for anything, and so can be NULL. |
| 350 // |
| 351 // TODO(ricea): Do this better. |
| 352 transaction_->SetBeforeProxyHeadersSentCallback( |
| 353 base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders, |
| 354 base::Unretained(network_delegate), |
| 355 static_cast<URLRequest*>(NULL))); |
| 356 // The above use of base::Unretained is safe because the NetworkDelegate has |
| 357 // to live at least as long as the HttpNetworkSession which has to live as |
| 358 // least as long as the HttpNetworkLayer which has to live at least as long |
| 359 // this HttpCache object. |
| 360 } |
| 361 |
| 362 DCHECK_EQ(0, request_.load_flags & LOAD_ASYNC_REVALIDATION); |
| 363 request_.load_flags |= LOAD_ASYNC_REVALIDATION; |
| 364 start_time_ = base::Time::Now(); |
| 365 // This use of base::Unretained is safe because |transaction_| is owned by |
| 366 // this object. |
| 367 read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this)); |
| 368 // This use of base::Unretained is safe as above. |
| 369 int rv = transaction_->Start( |
| 370 &request_, |
| 371 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)), |
| 372 net_log); |
| 373 |
| 374 if (rv == ERR_IO_PENDING) |
| 375 return; |
| 376 |
| 377 OnStarted(rv); |
| 378 } |
| 379 |
| 380 void HttpCache::AsyncValidation::OnStarted(int result) { |
| 381 if (result != OK) { |
| 382 DVLOG(1) << "Asynchronous transaction start failed for " << request_.url; |
| 383 Terminate(result); |
| 384 return; |
| 385 } |
| 386 |
| 387 while (transaction_->IsReadyToRestartForAuth()) { |
| 388 // This code is based on URLRequestHttpJob::RestartTransactionWithAuth, |
| 389 // however when we do this here cookies on the response will not be |
| 390 // stored. Fortunately only a tiny number of sites set cookies on 401 |
| 391 // responses, and none of them use stale-while-revalidate. |
| 392 result = transaction_->RestartWithAuth( |
| 393 AuthCredentials(), |
| 394 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this))); |
| 395 if (result == ERR_IO_PENDING) |
| 396 return; |
| 397 if (result != OK) { |
| 398 DVLOG(1) << "Synchronous transaction restart with auth failed for " |
| 399 << request_.url; |
| 400 Terminate(result); |
| 401 return; |
| 402 } |
| 403 } |
| 404 |
| 405 DoRead(); |
| 406 } |
| 407 |
| 408 void HttpCache::AsyncValidation::DoRead() { |
| 409 const size_t kBufSize = 4096; |
| 410 if (!buf_.get()) |
| 411 buf_ = new IOBuffer(kBufSize); |
| 412 |
| 413 int rv = 0; |
| 414 do { |
| 415 rv = transaction_->Read(buf_.get(), kBufSize, read_callback_); |
| 416 } while (rv > 0); |
| 417 |
| 418 if (rv == ERR_IO_PENDING) |
| 419 return; |
| 420 |
| 421 OnRead(rv); |
| 422 } |
| 423 |
| 424 void HttpCache::AsyncValidation::OnRead(int result) { |
| 425 if (result > 0) { |
| 426 DoRead(); |
| 427 return; |
| 428 } |
| 429 Terminate(result); |
| 430 } |
| 431 |
| 432 void HttpCache::AsyncValidation::Terminate(int result) { |
| 433 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result)) { |
| 434 // We should not attempt to access this resource asynchronously again until |
| 435 // the certificate problem has been resolved. |
| 436 // TODO(ricea): For ERR_SSL_CLIENT_AUTH_CERT_NEEDED, mark the entry as |
| 437 // requiring synchronous revalidation rather than just deleting it. Other |
| 438 // certificate errors cause the resource to be considered uncacheable |
| 439 // anyway. |
| 440 cache_->DoomEntry(transaction_->key(), transaction_.get()); |
| 441 } |
| 442 base::TimeDelta duration = base::Time::Now() - start_time_; |
| 443 UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationDuration", duration); |
| 444 transaction_->net_log().EndEventWithNetErrorCode( |
| 445 NetLog::TYPE_ASYNC_REVALIDATION, result); |
| 446 cache_->DeleteAsyncValidation(cache_->GenerateCacheKey(&request_)); |
| 447 // |this| is deleted. |
| 448 } |
| 449 |
| 450 //----------------------------------------------------------------------------- |
295 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params, | 451 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params, |
296 BackendFactory* backend_factory) | 452 BackendFactory* backend_factory) |
297 : net_log_(params.net_log), | 453 : net_log_(params.net_log), |
298 backend_factory_(backend_factory), | 454 backend_factory_(backend_factory), |
299 building_backend_(false), | 455 building_backend_(false), |
300 bypass_lock_for_test_(false), | 456 bypass_lock_for_test_(false), |
| 457 use_stale_while_revalidate_(params.use_stale_while_revalidate), |
301 mode_(NORMAL), | 458 mode_(NORMAL), |
302 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))), | 459 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))), |
303 weak_factory_(this) { | 460 weak_factory_(this) { |
304 SetupQuicServerInfoFactory(network_layer_->GetSession()); | 461 SetupQuicServerInfoFactory(network_layer_->GetSession()); |
305 } | 462 } |
306 | 463 |
307 | 464 |
308 // This call doesn't change the shared |session|'s QuicServerInfoFactory because | 465 // This call doesn't change the shared |session|'s QuicServerInfoFactory because |
309 // |session| is shared. | 466 // |session| is shared. |
310 HttpCache::HttpCache(HttpNetworkSession* session, | 467 HttpCache::HttpCache(HttpNetworkSession* session, |
311 BackendFactory* backend_factory) | 468 BackendFactory* backend_factory) |
312 : net_log_(session->net_log()), | 469 : net_log_(session->net_log()), |
313 backend_factory_(backend_factory), | 470 backend_factory_(backend_factory), |
314 building_backend_(false), | 471 building_backend_(false), |
315 bypass_lock_for_test_(false), | 472 bypass_lock_for_test_(false), |
| 473 use_stale_while_revalidate_(session->params().use_stale_while_revalidate), |
316 mode_(NORMAL), | 474 mode_(NORMAL), |
317 network_layer_(new HttpNetworkLayer(session)), | 475 network_layer_(new HttpNetworkLayer(session)), |
318 weak_factory_(this) { | 476 weak_factory_(this) { |
319 } | 477 } |
320 | 478 |
321 HttpCache::HttpCache(HttpTransactionFactory* network_layer, | 479 HttpCache::HttpCache(HttpTransactionFactory* network_layer, |
322 NetLog* net_log, | 480 NetLog* net_log, |
323 BackendFactory* backend_factory) | 481 BackendFactory* backend_factory) |
324 : net_log_(net_log), | 482 : net_log_(net_log), |
325 backend_factory_(backend_factory), | 483 backend_factory_(backend_factory), |
326 building_backend_(false), | 484 building_backend_(false), |
327 bypass_lock_for_test_(false), | 485 bypass_lock_for_test_(false), |
| 486 use_stale_while_revalidate_(false), |
328 mode_(NORMAL), | 487 mode_(NORMAL), |
329 network_layer_(network_layer), | 488 network_layer_(network_layer), |
330 weak_factory_(this) { | 489 weak_factory_(this) { |
331 SetupQuicServerInfoFactory(network_layer_->GetSession()); | 490 SetupQuicServerInfoFactory(network_layer_->GetSession()); |
| 491 HttpNetworkSession* session = network_layer_->GetSession(); |
| 492 if (session) |
| 493 use_stale_while_revalidate_ = session->params().use_stale_while_revalidate; |
332 } | 494 } |
333 | 495 |
334 HttpCache::~HttpCache() { | 496 HttpCache::~HttpCache() { |
335 // Transactions should see an invalid cache after this point; otherwise they | 497 // Transactions should see an invalid cache after this point; otherwise they |
336 // could see an inconsistent object (half destroyed). | 498 // could see an inconsistent object (half destroyed). |
337 weak_factory_.InvalidateWeakPtrs(); | 499 weak_factory_.InvalidateWeakPtrs(); |
338 | 500 |
339 // If we have any active entries remaining, then we need to deactivate them. | 501 // 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 | 502 // 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 | 503 // won't run (due to our destruction), we can simply ignore the corresponding |
342 // will_process_pending_queue flag. | 504 // will_process_pending_queue flag. |
343 while (!active_entries_.empty()) { | 505 while (!active_entries_.empty()) { |
344 ActiveEntry* entry = active_entries_.begin()->second; | 506 ActiveEntry* entry = active_entries_.begin()->second; |
345 entry->will_process_pending_queue = false; | 507 entry->will_process_pending_queue = false; |
346 entry->pending_queue.clear(); | 508 entry->pending_queue.clear(); |
347 entry->readers.clear(); | 509 entry->readers.clear(); |
348 entry->writer = NULL; | 510 entry->writer = NULL; |
349 DeactivateEntry(entry); | 511 DeactivateEntry(entry); |
350 } | 512 } |
351 | 513 |
352 STLDeleteElements(&doomed_entries_); | 514 STLDeleteElements(&doomed_entries_); |
| 515 STLDeleteValues(&async_validations_); |
353 | 516 |
354 // Before deleting pending_ops_, we have to make sure that the disk cache is | 517 // 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. | 518 // done with said operations, or it will attempt to use deleted data. |
356 cert_cache_.reset(); | 519 cert_cache_.reset(); |
357 disk_cache_.reset(); | 520 disk_cache_.reset(); |
358 | 521 |
359 PendingOpsMap::iterator pending_it = pending_ops_.begin(); | 522 PendingOpsMap::iterator pending_it = pending_ops_.begin(); |
360 for (; pending_it != pending_ops_.end(); ++pending_it) { | 523 for (; pending_it != pending_ops_.end(); ++pending_it) { |
361 // We are not notifying the transactions about the cache going away, even | 524 // We are not notifying the transactions about the cache going away, even |
362 // though they are waiting for a callback that will never fire. | 525 // though they are waiting for a callback that will never fire. |
(...skipping 668 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1031 // not delete the entry before OnProcessPendingQueue runs. | 1194 // not delete the entry before OnProcessPendingQueue runs. |
1032 if (entry->will_process_pending_queue) | 1195 if (entry->will_process_pending_queue) |
1033 return; | 1196 return; |
1034 entry->will_process_pending_queue = true; | 1197 entry->will_process_pending_queue = true; |
1035 | 1198 |
1036 base::MessageLoop::current()->PostTask( | 1199 base::MessageLoop::current()->PostTask( |
1037 FROM_HERE, | 1200 FROM_HERE, |
1038 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry)); | 1201 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry)); |
1039 } | 1202 } |
1040 | 1203 |
| 1204 void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request, |
| 1205 const BoundNetLog& net_log) { |
| 1206 DCHECK(use_stale_while_revalidate_); |
| 1207 std::string key = GenerateCacheKey(&original_request); |
| 1208 AsyncValidation* async_validation = |
| 1209 new AsyncValidation(original_request, this); |
| 1210 typedef AsyncValidationMap::value_type AsyncValidationKeyValue; |
| 1211 bool insert_ok = |
| 1212 async_validations_.insert(AsyncValidationKeyValue(key, async_validation)) |
| 1213 .second; |
| 1214 if (!insert_ok) { |
| 1215 DVLOG(1) << "Harmless race condition detected on URL " |
| 1216 << original_request.url << "; discarding redundant revalidation."; |
| 1217 delete async_validation; |
| 1218 return; |
| 1219 } |
| 1220 HttpNetworkSession* network_session = GetSession(); |
| 1221 NetworkDelegate* network_delegate = NULL; |
| 1222 if (network_session) |
| 1223 network_delegate = network_session->network_delegate(); |
| 1224 scoped_ptr<HttpTransaction> transaction; |
| 1225 CreateTransaction(IDLE, &transaction); |
| 1226 scoped_ptr<Transaction> downcast_transaction( |
| 1227 static_cast<Transaction*>(transaction.release())); |
| 1228 async_validation->Start( |
| 1229 net_log, downcast_transaction.Pass(), network_delegate); |
| 1230 // |async_validation| may have been deleted here. |
| 1231 } |
| 1232 |
| 1233 void HttpCache::DeleteAsyncValidation(const std::string& url) { |
| 1234 AsyncValidationMap::iterator it = async_validations_.find(url); |
| 1235 CHECK(it != async_validations_.end()); // security-critical invariant |
| 1236 AsyncValidation* async_validation = it->second; |
| 1237 async_validations_.erase(it); |
| 1238 delete async_validation; |
| 1239 } |
| 1240 |
1041 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { | 1241 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { |
1042 entry->will_process_pending_queue = false; | 1242 entry->will_process_pending_queue = false; |
1043 DCHECK(!entry->writer); | 1243 DCHECK(!entry->writer); |
1044 | 1244 |
1045 // If no one is interested in this entry, then we can deactivate it. | 1245 // If no one is interested in this entry, then we can deactivate it. |
1046 if (entry->pending_queue.empty()) { | 1246 if (entry->pending_queue.empty()) { |
1047 if (entry->readers.empty()) | 1247 if (entry->readers.empty()) |
1048 DestroyEntry(entry); | 1248 DestroyEntry(entry); |
1049 return; | 1249 return; |
1050 } | 1250 } |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1201 building_backend_ = false; | 1401 building_backend_ = false; |
1202 DeletePendingOp(pending_op); | 1402 DeletePendingOp(pending_op); |
1203 } | 1403 } |
1204 | 1404 |
1205 // The cache may be gone when we return from the callback. | 1405 // The cache may be gone when we return from the callback. |
1206 if (!item->DoCallback(result, disk_cache_.get())) | 1406 if (!item->DoCallback(result, disk_cache_.get())) |
1207 item->NotifyTransaction(result, NULL); | 1407 item->NotifyTransaction(result, NULL); |
1208 } | 1408 } |
1209 | 1409 |
1210 } // namespace net | 1410 } // namespace net |
OLD | NEW |