OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/http/http_cache.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/compiler_specific.h" | |
10 | |
11 #if defined(OS_POSIX) | |
12 #include <unistd.h> | |
13 #endif | |
14 | |
15 #include "base/bind.h" | |
16 #include "base/bind_helpers.h" | |
17 #include "base/callback.h" | |
18 #include "base/files/file_util.h" | |
19 #include "base/format_macros.h" | |
20 #include "base/location.h" | |
21 #include "base/memory/ref_counted.h" | |
22 #include "base/message_loop/message_loop.h" | |
23 #include "base/metrics/field_trial.h" | |
24 #include "base/metrics/histogram.h" | |
25 #include "base/pickle.h" | |
26 #include "base/profiler/scoped_tracker.h" | |
27 #include "base/stl_util.h" | |
28 #include "base/strings/string_number_conversions.h" | |
29 #include "base/strings/string_util.h" | |
30 #include "base/strings/stringprintf.h" | |
31 #include "base/threading/worker_pool.h" | |
32 #include "base/time/default_clock.h" | |
33 #include "base/time/time.h" | |
34 #include "net/base/cache_type.h" | |
35 #include "net/base/io_buffer.h" | |
36 #include "net/base/load_flags.h" | |
37 #include "net/base/net_errors.h" | |
38 #include "net/base/network_delegate.h" | |
39 #include "net/base/upload_data_stream.h" | |
40 #include "net/disk_cache/disk_cache.h" | |
41 #include "net/http/disk_based_cert_cache.h" | |
42 #include "net/http/disk_cache_based_quic_server_info.h" | |
43 #include "net/http/http_cache_transaction.h" | |
44 #include "net/http/http_network_layer.h" | |
45 #include "net/http/http_network_session.h" | |
46 #include "net/http/http_request_info.h" | |
47 #include "net/http/http_response_headers.h" | |
48 #include "net/http/http_response_info.h" | |
49 #include "net/http/http_util.h" | |
50 #include "net/quic/crypto/quic_server_info.h" | |
51 | |
52 namespace { | |
53 | |
54 bool UseCertCache() { | |
55 return base::FieldTrialList::FindFullName("CertCacheTrial") == | |
56 "ExperimentGroup"; | |
57 } | |
58 | |
59 // Adaptor to delete a file on a worker thread. | |
60 void DeletePath(base::FilePath path) { | |
61 base::DeleteFile(path, false); | |
62 } | |
63 | |
64 } // namespace | |
65 | |
66 namespace net { | |
67 | |
68 HttpCache::DefaultBackend::DefaultBackend( | |
69 CacheType type, | |
70 BackendType backend_type, | |
71 const base::FilePath& path, | |
72 int max_bytes, | |
73 const scoped_refptr<base::SingleThreadTaskRunner>& thread) | |
74 : type_(type), | |
75 backend_type_(backend_type), | |
76 path_(path), | |
77 max_bytes_(max_bytes), | |
78 thread_(thread) { | |
79 } | |
80 | |
81 HttpCache::DefaultBackend::~DefaultBackend() {} | |
82 | |
83 // static | |
84 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) { | |
85 return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT, | |
86 base::FilePath(), max_bytes, NULL); | |
87 } | |
88 | |
89 int HttpCache::DefaultBackend::CreateBackend( | |
90 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend, | |
91 const CompletionCallback& callback) { | |
92 DCHECK_GE(max_bytes_, 0); | |
93 return disk_cache::CreateCacheBackend(type_, | |
94 backend_type_, | |
95 path_, | |
96 max_bytes_, | |
97 true, | |
98 thread_, | |
99 net_log, | |
100 backend, | |
101 callback); | |
102 } | |
103 | |
104 //----------------------------------------------------------------------------- | |
105 | |
106 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry) | |
107 : disk_entry(entry), | |
108 writer(NULL), | |
109 will_process_pending_queue(false), | |
110 doomed(false) { | |
111 } | |
112 | |
113 HttpCache::ActiveEntry::~ActiveEntry() { | |
114 if (disk_entry) { | |
115 disk_entry->Close(); | |
116 disk_entry = NULL; | |
117 } | |
118 } | |
119 | |
120 //----------------------------------------------------------------------------- | |
121 | |
122 // This structure keeps track of work items that are attempting to create or | |
123 // open cache entries or the backend itself. | |
124 struct HttpCache::PendingOp { | |
125 PendingOp() : disk_entry(NULL), writer(NULL) {} | |
126 ~PendingOp() {} | |
127 | |
128 disk_cache::Entry* disk_entry; | |
129 scoped_ptr<disk_cache::Backend> backend; | |
130 WorkItem* writer; | |
131 CompletionCallback callback; // BackendCallback. | |
132 WorkItemList pending_queue; | |
133 }; | |
134 | |
135 //----------------------------------------------------------------------------- | |
136 | |
137 // The type of operation represented by a work item. | |
138 enum WorkItemOperation { | |
139 WI_CREATE_BACKEND, | |
140 WI_OPEN_ENTRY, | |
141 WI_CREATE_ENTRY, | |
142 WI_DOOM_ENTRY | |
143 }; | |
144 | |
145 // A work item encapsulates a single request to the backend with all the | |
146 // information needed to complete that request. | |
147 class HttpCache::WorkItem { | |
148 public: | |
149 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry) | |
150 : operation_(operation), | |
151 trans_(trans), | |
152 entry_(entry), | |
153 backend_(NULL) {} | |
154 WorkItem(WorkItemOperation operation, Transaction* trans, | |
155 const net::CompletionCallback& cb, disk_cache::Backend** backend) | |
156 : operation_(operation), | |
157 trans_(trans), | |
158 entry_(NULL), | |
159 callback_(cb), | |
160 backend_(backend) {} | |
161 ~WorkItem() {} | |
162 | |
163 // Calls back the transaction with the result of the operation. | |
164 void NotifyTransaction(int result, ActiveEntry* entry) { | |
165 DCHECK(!entry || entry->disk_entry); | |
166 if (entry_) | |
167 *entry_ = entry; | |
168 if (trans_) | |
169 trans_->io_callback().Run(result); | |
170 } | |
171 | |
172 // Notifies the caller about the operation completion. Returns true if the | |
173 // callback was invoked. | |
174 bool DoCallback(int result, disk_cache::Backend* backend) { | |
175 if (backend_) | |
176 *backend_ = backend; | |
177 if (!callback_.is_null()) { | |
178 callback_.Run(result); | |
179 return true; | |
180 } | |
181 return false; | |
182 } | |
183 | |
184 WorkItemOperation operation() { return operation_; } | |
185 void ClearTransaction() { trans_ = NULL; } | |
186 void ClearEntry() { entry_ = NULL; } | |
187 void ClearCallback() { callback_.Reset(); } | |
188 bool Matches(Transaction* trans) const { return trans == trans_; } | |
189 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); } | |
190 | |
191 private: | |
192 WorkItemOperation operation_; | |
193 Transaction* trans_; | |
194 ActiveEntry** entry_; | |
195 net::CompletionCallback callback_; // User callback. | |
196 disk_cache::Backend** backend_; | |
197 }; | |
198 | |
199 //----------------------------------------------------------------------------- | |
200 | |
201 // This class encapsulates a transaction whose only purpose is to write metadata | |
202 // to a given entry. | |
203 class HttpCache::MetadataWriter { | |
204 public: | |
205 explicit MetadataWriter(HttpCache::Transaction* trans) | |
206 : transaction_(trans), | |
207 verified_(false), | |
208 buf_len_(0) { | |
209 } | |
210 | |
211 ~MetadataWriter() {} | |
212 | |
213 // Implements the bulk of HttpCache::WriteMetadata. | |
214 void Write(const GURL& url, | |
215 double expected_response_time, | |
216 IOBuffer* buf, | |
217 int buf_len); | |
218 | |
219 private: | |
220 void VerifyResponse(int result); | |
221 void SelfDestroy(); | |
222 void OnIOComplete(int result); | |
223 | |
224 scoped_ptr<HttpCache::Transaction> transaction_; | |
225 bool verified_; | |
226 scoped_refptr<IOBuffer> buf_; | |
227 int buf_len_; | |
228 double expected_response_time_; | |
229 HttpRequestInfo request_info_; | |
230 DISALLOW_COPY_AND_ASSIGN(MetadataWriter); | |
231 }; | |
232 | |
233 void HttpCache::MetadataWriter::Write(const GURL& url, | |
234 double expected_response_time, | |
235 IOBuffer* buf, | |
236 int buf_len) { | |
237 DCHECK_GT(buf_len, 0); | |
238 DCHECK(buf); | |
239 DCHECK(buf->data()); | |
240 request_info_.url = url; | |
241 request_info_.method = "GET"; | |
242 request_info_.load_flags = LOAD_ONLY_FROM_CACHE; | |
243 | |
244 expected_response_time_ = expected_response_time; | |
245 buf_ = buf; | |
246 buf_len_ = buf_len; | |
247 verified_ = false; | |
248 | |
249 int rv = transaction_->Start( | |
250 &request_info_, | |
251 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)), | |
252 BoundNetLog()); | |
253 if (rv != ERR_IO_PENDING) | |
254 VerifyResponse(rv); | |
255 } | |
256 | |
257 void HttpCache::MetadataWriter::VerifyResponse(int result) { | |
258 verified_ = true; | |
259 if (result != OK) | |
260 return SelfDestroy(); | |
261 | |
262 const HttpResponseInfo* response_info = transaction_->GetResponseInfo(); | |
263 DCHECK(response_info->was_cached); | |
264 if (response_info->response_time.ToDoubleT() != expected_response_time_) | |
265 return SelfDestroy(); | |
266 | |
267 result = transaction_->WriteMetadata( | |
268 buf_.get(), | |
269 buf_len_, | |
270 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this))); | |
271 if (result != ERR_IO_PENDING) | |
272 SelfDestroy(); | |
273 } | |
274 | |
275 void HttpCache::MetadataWriter::SelfDestroy() { | |
276 delete this; | |
277 } | |
278 | |
279 void HttpCache::MetadataWriter::OnIOComplete(int result) { | |
280 if (!verified_) | |
281 return VerifyResponse(result); | |
282 SelfDestroy(); | |
283 } | |
284 | |
285 //----------------------------------------------------------------------------- | |
286 | |
287 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory { | |
288 public: | |
289 explicit QuicServerInfoFactoryAdaptor(HttpCache* http_cache) | |
290 : http_cache_(http_cache) { | |
291 } | |
292 | |
293 QuicServerInfo* GetForServer(const QuicServerId& server_id) override { | |
294 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_); | |
295 } | |
296 | |
297 private: | |
298 HttpCache* const http_cache_; | |
299 }; | |
300 | |
301 //----------------------------------------------------------------------------- | |
302 | |
303 class HttpCache::AsyncValidation { | |
304 public: | |
305 AsyncValidation(const HttpRequestInfo& original_request, HttpCache* cache) | |
306 : request_(original_request), cache_(cache) {} | |
307 ~AsyncValidation() {} | |
308 | |
309 void Start(const BoundNetLog& net_log, | |
310 scoped_ptr<Transaction> transaction, | |
311 NetworkDelegate* network_delegate); | |
312 | |
313 private: | |
314 void OnStarted(int result); | |
315 void DoRead(); | |
316 void OnRead(int result); | |
317 | |
318 // Terminate this request with net error code |result|. Logs the transaction | |
319 // result and asks HttpCache to delete this object. | |
320 // If there was a client or server certificate error, it cannot be recovered | |
321 // asynchronously, so we need to prevent future attempts to asynchronously | |
322 // fetch the resource. In this case, the cache entry is doomed. | |
323 void Terminate(int result); | |
324 | |
325 HttpRequestInfo request_; | |
326 scoped_refptr<IOBuffer> buf_; | |
327 CompletionCallback read_callback_; | |
328 scoped_ptr<Transaction> transaction_; | |
329 base::Time start_time_; | |
330 | |
331 // The HttpCache object owns this object. This object is always deleted before | |
332 // the pointer to the cache becomes invalid. | |
333 HttpCache* cache_; | |
334 | |
335 DISALLOW_COPY_AND_ASSIGN(AsyncValidation); | |
336 }; | |
337 | |
338 void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log, | |
339 scoped_ptr<Transaction> transaction, | |
340 NetworkDelegate* network_delegate) { | |
341 transaction_ = transaction.Pass(); | |
342 if (network_delegate) { | |
343 // This code is necessary to enable async transactions to pass over the | |
344 // data-reduction proxy. This is a violation of the "once-and-only-once" | |
345 // principle, since it copies code from URLRequestHttpJob. We cannot use the | |
346 // original callback passed to HttpCache::Transaction by URLRequestHttpJob | |
347 // as it will only be valid as long as the URLRequestHttpJob object is | |
348 // alive, and that object will be deleted as soon as the synchronous request | |
349 // completes. | |
350 // | |
351 // This code is also an encapsulation violation. We are exploiting the fact | |
352 // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never | |
353 // actually used for anything, and so can be NULL. | |
354 // | |
355 // TODO(ricea): Do this better. | |
356 transaction_->SetBeforeProxyHeadersSentCallback( | |
357 base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders, | |
358 base::Unretained(network_delegate), | |
359 static_cast<URLRequest*>(NULL))); | |
360 // The above use of base::Unretained is safe because the NetworkDelegate has | |
361 // to live at least as long as the HttpNetworkSession which has to live as | |
362 // least as long as the HttpNetworkLayer which has to live at least as long | |
363 // this HttpCache object. | |
364 } | |
365 | |
366 DCHECK_EQ(0, request_.load_flags & LOAD_ASYNC_REVALIDATION); | |
367 request_.load_flags |= LOAD_ASYNC_REVALIDATION; | |
368 start_time_ = cache_->clock()->Now(); | |
369 // This use of base::Unretained is safe because |transaction_| is owned by | |
370 // this object. | |
371 read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this)); | |
372 // This use of base::Unretained is safe as above. | |
373 int rv = transaction_->Start( | |
374 &request_, | |
375 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)), | |
376 net_log); | |
377 | |
378 if (rv == ERR_IO_PENDING) | |
379 return; | |
380 | |
381 OnStarted(rv); | |
382 } | |
383 | |
384 void HttpCache::AsyncValidation::OnStarted(int result) { | |
385 if (result != OK) { | |
386 DVLOG(1) << "Asynchronous transaction start failed for " << request_.url; | |
387 Terminate(result); | |
388 return; | |
389 } | |
390 | |
391 while (transaction_->IsReadyToRestartForAuth()) { | |
392 // This code is based on URLRequestHttpJob::RestartTransactionWithAuth, | |
393 // however when we do this here cookies on the response will not be | |
394 // stored. Fortunately only a tiny number of sites set cookies on 401 | |
395 // responses, and none of them use stale-while-revalidate. | |
396 result = transaction_->RestartWithAuth( | |
397 AuthCredentials(), | |
398 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this))); | |
399 if (result == ERR_IO_PENDING) | |
400 return; | |
401 if (result != OK) { | |
402 DVLOG(1) << "Synchronous transaction restart with auth failed for " | |
403 << request_.url; | |
404 Terminate(result); | |
405 return; | |
406 } | |
407 } | |
408 | |
409 DoRead(); | |
410 } | |
411 | |
412 void HttpCache::AsyncValidation::DoRead() { | |
413 const size_t kBufSize = 4096; | |
414 if (!buf_.get()) | |
415 buf_ = new IOBuffer(kBufSize); | |
416 | |
417 int rv = 0; | |
418 do { | |
419 rv = transaction_->Read(buf_.get(), kBufSize, read_callback_); | |
420 } while (rv > 0); | |
421 | |
422 if (rv == ERR_IO_PENDING) | |
423 return; | |
424 | |
425 OnRead(rv); | |
426 } | |
427 | |
428 void HttpCache::AsyncValidation::OnRead(int result) { | |
429 if (result > 0) { | |
430 DoRead(); | |
431 return; | |
432 } | |
433 Terminate(result); | |
434 } | |
435 | |
436 void HttpCache::AsyncValidation::Terminate(int result) { | |
437 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result)) { | |
438 // We should not attempt to access this resource asynchronously again until | |
439 // the certificate problem has been resolved. | |
440 // TODO(ricea): For ERR_SSL_CLIENT_AUTH_CERT_NEEDED, mark the entry as | |
441 // requiring synchronous revalidation rather than just deleting it. Other | |
442 // certificate errors cause the resource to be considered uncacheable | |
443 // anyway. | |
444 cache_->DoomEntry(transaction_->key(), transaction_.get()); | |
445 } | |
446 base::TimeDelta duration = cache_->clock()->Now() - start_time_; | |
447 UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationDuration", duration); | |
448 transaction_->net_log().EndEventWithNetErrorCode( | |
449 NetLog::TYPE_ASYNC_REVALIDATION, result); | |
450 cache_->DeleteAsyncValidation(cache_->GenerateCacheKey(&request_)); | |
451 // |this| is deleted. | |
452 } | |
453 | |
454 //----------------------------------------------------------------------------- | |
455 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params, | |
456 BackendFactory* backend_factory) | |
457 : net_log_(params.net_log), | |
458 backend_factory_(backend_factory), | |
459 building_backend_(false), | |
460 bypass_lock_for_test_(false), | |
461 fail_conditionalization_for_test_(false), | |
462 use_stale_while_revalidate_(params.use_stale_while_revalidate), | |
463 mode_(NORMAL), | |
464 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))), | |
465 clock_(new base::DefaultClock()), | |
466 weak_factory_(this) { | |
467 SetupQuicServerInfoFactory(network_layer_->GetSession()); | |
468 } | |
469 | |
470 | |
471 // This call doesn't change the shared |session|'s QuicServerInfoFactory because | |
472 // |session| is shared. | |
473 HttpCache::HttpCache(HttpNetworkSession* session, | |
474 BackendFactory* backend_factory) | |
475 : net_log_(session->net_log()), | |
476 backend_factory_(backend_factory), | |
477 building_backend_(false), | |
478 bypass_lock_for_test_(false), | |
479 fail_conditionalization_for_test_(false), | |
480 use_stale_while_revalidate_(session->params().use_stale_while_revalidate), | |
481 mode_(NORMAL), | |
482 network_layer_(new HttpNetworkLayer(session)), | |
483 clock_(new base::DefaultClock()), | |
484 weak_factory_(this) { | |
485 } | |
486 | |
487 HttpCache::HttpCache(HttpTransactionFactory* network_layer, | |
488 NetLog* net_log, | |
489 BackendFactory* backend_factory) | |
490 : net_log_(net_log), | |
491 backend_factory_(backend_factory), | |
492 building_backend_(false), | |
493 bypass_lock_for_test_(false), | |
494 fail_conditionalization_for_test_(false), | |
495 use_stale_while_revalidate_(false), | |
496 mode_(NORMAL), | |
497 network_layer_(network_layer), | |
498 clock_(new base::DefaultClock()), | |
499 weak_factory_(this) { | |
500 SetupQuicServerInfoFactory(network_layer_->GetSession()); | |
501 HttpNetworkSession* session = network_layer_->GetSession(); | |
502 if (session) | |
503 use_stale_while_revalidate_ = session->params().use_stale_while_revalidate; | |
504 } | |
505 | |
506 HttpCache::~HttpCache() { | |
507 // Transactions should see an invalid cache after this point; otherwise they | |
508 // could see an inconsistent object (half destroyed). | |
509 weak_factory_.InvalidateWeakPtrs(); | |
510 | |
511 // If we have any active entries remaining, then we need to deactivate them. | |
512 // We may have some pending calls to OnProcessPendingQueue, but since those | |
513 // won't run (due to our destruction), we can simply ignore the corresponding | |
514 // will_process_pending_queue flag. | |
515 while (!active_entries_.empty()) { | |
516 ActiveEntry* entry = active_entries_.begin()->second; | |
517 entry->will_process_pending_queue = false; | |
518 entry->pending_queue.clear(); | |
519 entry->readers.clear(); | |
520 entry->writer = NULL; | |
521 DeactivateEntry(entry); | |
522 } | |
523 | |
524 STLDeleteElements(&doomed_entries_); | |
525 STLDeleteValues(&async_validations_); | |
526 | |
527 // Before deleting pending_ops_, we have to make sure that the disk cache is | |
528 // done with said operations, or it will attempt to use deleted data. | |
529 cert_cache_.reset(); | |
530 disk_cache_.reset(); | |
531 | |
532 PendingOpsMap::iterator pending_it = pending_ops_.begin(); | |
533 for (; pending_it != pending_ops_.end(); ++pending_it) { | |
534 // We are not notifying the transactions about the cache going away, even | |
535 // though they are waiting for a callback that will never fire. | |
536 PendingOp* pending_op = pending_it->second; | |
537 delete pending_op->writer; | |
538 bool delete_pending_op = true; | |
539 if (building_backend_) { | |
540 // If we don't have a backend, when its construction finishes it will | |
541 // deliver the callbacks. | |
542 if (!pending_op->callback.is_null()) { | |
543 // If not null, the callback will delete the pending operation later. | |
544 delete_pending_op = false; | |
545 } | |
546 } else { | |
547 pending_op->callback.Reset(); | |
548 } | |
549 | |
550 STLDeleteElements(&pending_op->pending_queue); | |
551 if (delete_pending_op) | |
552 delete pending_op; | |
553 } | |
554 } | |
555 | |
556 int HttpCache::GetBackend(disk_cache::Backend** backend, | |
557 const CompletionCallback& callback) { | |
558 DCHECK(!callback.is_null()); | |
559 | |
560 if (disk_cache_.get()) { | |
561 *backend = disk_cache_.get(); | |
562 return OK; | |
563 } | |
564 | |
565 return CreateBackend(backend, callback); | |
566 } | |
567 | |
568 disk_cache::Backend* HttpCache::GetCurrentBackend() const { | |
569 return disk_cache_.get(); | |
570 } | |
571 | |
572 // static | |
573 bool HttpCache::ParseResponseInfo(const char* data, int len, | |
574 HttpResponseInfo* response_info, | |
575 bool* response_truncated) { | |
576 Pickle pickle(data, len); | |
577 return response_info->InitFromPickle(pickle, response_truncated); | |
578 } | |
579 | |
580 void HttpCache::WriteMetadata(const GURL& url, | |
581 RequestPriority priority, | |
582 double expected_response_time, | |
583 IOBuffer* buf, | |
584 int buf_len) { | |
585 if (!buf_len) | |
586 return; | |
587 | |
588 // Do lazy initialization of disk cache if needed. | |
589 if (!disk_cache_.get()) { | |
590 // We don't care about the result. | |
591 CreateBackend(NULL, net::CompletionCallback()); | |
592 } | |
593 | |
594 HttpCache::Transaction* trans = | |
595 new HttpCache::Transaction(priority, this); | |
596 MetadataWriter* writer = new MetadataWriter(trans); | |
597 | |
598 // The writer will self destruct when done. | |
599 writer->Write(url, expected_response_time, buf, buf_len); | |
600 } | |
601 | |
602 void HttpCache::CloseAllConnections() { | |
603 HttpNetworkSession* session = GetSession(); | |
604 if (session) | |
605 session->CloseAllConnections(); | |
606 } | |
607 | |
608 void HttpCache::CloseIdleConnections() { | |
609 HttpNetworkSession* session = GetSession(); | |
610 if (session) | |
611 session->CloseIdleConnections(); | |
612 } | |
613 | |
614 void HttpCache::OnExternalCacheHit(const GURL& url, | |
615 const std::string& http_method) { | |
616 if (!disk_cache_.get() || mode_ == DISABLE) | |
617 return; | |
618 | |
619 HttpRequestInfo request_info; | |
620 request_info.url = url; | |
621 request_info.method = http_method; | |
622 std::string key = GenerateCacheKey(&request_info); | |
623 disk_cache_->OnExternalCacheHit(key); | |
624 } | |
625 | |
626 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) { | |
627 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes") | |
628 return; | |
629 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true); | |
630 } | |
631 | |
632 int HttpCache::CreateTransaction(RequestPriority priority, | |
633 scoped_ptr<HttpTransaction>* trans) { | |
634 // Do lazy initialization of disk cache if needed. | |
635 if (!disk_cache_.get()) { | |
636 // We don't care about the result. | |
637 CreateBackend(NULL, net::CompletionCallback()); | |
638 } | |
639 | |
640 HttpCache::Transaction* transaction = | |
641 new HttpCache::Transaction(priority, this); | |
642 if (bypass_lock_for_test_) | |
643 transaction->BypassLockForTest(); | |
644 if (fail_conditionalization_for_test_) | |
645 transaction->FailConditionalizationForTest(); | |
646 | |
647 trans->reset(transaction); | |
648 return OK; | |
649 } | |
650 | |
651 HttpCache* HttpCache::GetCache() { | |
652 return this; | |
653 } | |
654 | |
655 HttpNetworkSession* HttpCache::GetSession() { | |
656 return network_layer_->GetSession(); | |
657 } | |
658 | |
659 scoped_ptr<HttpTransactionFactory> | |
660 HttpCache::SetHttpNetworkTransactionFactoryForTesting( | |
661 scoped_ptr<HttpTransactionFactory> new_network_layer) { | |
662 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass()); | |
663 network_layer_ = new_network_layer.Pass(); | |
664 return old_network_layer.Pass(); | |
665 } | |
666 | |
667 //----------------------------------------------------------------------------- | |
668 | |
669 int HttpCache::CreateBackend(disk_cache::Backend** backend, | |
670 const net::CompletionCallback& callback) { | |
671 if (!backend_factory_.get()) | |
672 return ERR_FAILED; | |
673 | |
674 building_backend_ = true; | |
675 | |
676 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback, | |
677 backend)); | |
678 | |
679 // This is the only operation that we can do that is not related to any given | |
680 // entry, so we use an empty key for it. | |
681 PendingOp* pending_op = GetPendingOp(std::string()); | |
682 if (pending_op->writer) { | |
683 if (!callback.is_null()) | |
684 pending_op->pending_queue.push_back(item.release()); | |
685 return ERR_IO_PENDING; | |
686 } | |
687 | |
688 DCHECK(pending_op->pending_queue.empty()); | |
689 | |
690 pending_op->writer = item.release(); | |
691 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, | |
692 GetWeakPtr(), pending_op); | |
693 | |
694 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend, | |
695 pending_op->callback); | |
696 if (rv != ERR_IO_PENDING) { | |
697 pending_op->writer->ClearCallback(); | |
698 pending_op->callback.Run(rv); | |
699 } | |
700 | |
701 return rv; | |
702 } | |
703 | |
704 int HttpCache::GetBackendForTransaction(Transaction* trans) { | |
705 if (disk_cache_.get()) | |
706 return OK; | |
707 | |
708 if (!building_backend_) | |
709 return ERR_FAILED; | |
710 | |
711 WorkItem* item = new WorkItem( | |
712 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL); | |
713 PendingOp* pending_op = GetPendingOp(std::string()); | |
714 DCHECK(pending_op->writer); | |
715 pending_op->pending_queue.push_back(item); | |
716 return ERR_IO_PENDING; | |
717 } | |
718 | |
719 // Generate a key that can be used inside the cache. | |
720 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) { | |
721 // Strip out the reference, username, and password sections of the URL. | |
722 std::string url = HttpUtil::SpecForRequest(request->url); | |
723 | |
724 DCHECK(mode_ != DISABLE); | |
725 if (mode_ == NORMAL) { | |
726 // No valid URL can begin with numerals, so we should not have to worry | |
727 // about collisions with normal URLs. | |
728 if (request->upload_data_stream && | |
729 request->upload_data_stream->identifier()) { | |
730 url.insert(0, base::StringPrintf( | |
731 "%" PRId64 "/", request->upload_data_stream->identifier())); | |
732 } | |
733 return url; | |
734 } | |
735 | |
736 // In playback and record mode, we cache everything. | |
737 | |
738 // Lazily initialize. | |
739 if (playback_cache_map_ == NULL) | |
740 playback_cache_map_.reset(new PlaybackCacheMap()); | |
741 | |
742 // Each time we request an item from the cache, we tag it with a | |
743 // generation number. During playback, multiple fetches for the same | |
744 // item will use the same generation number and pull the proper | |
745 // instance of an URL from the cache. | |
746 int generation = 0; | |
747 DCHECK(playback_cache_map_ != NULL); | |
748 if (playback_cache_map_->find(url) != playback_cache_map_->end()) | |
749 generation = (*playback_cache_map_)[url]; | |
750 (*playback_cache_map_)[url] = generation + 1; | |
751 | |
752 // The key into the cache is GENERATION # + METHOD + URL. | |
753 std::string result = base::IntToString(generation); | |
754 result.append(request->method); | |
755 result.append(url); | |
756 return result; | |
757 } | |
758 | |
759 void HttpCache::DoomActiveEntry(const std::string& key) { | |
760 ActiveEntriesMap::iterator it = active_entries_.find(key); | |
761 if (it == active_entries_.end()) | |
762 return; | |
763 | |
764 // This is not a performance critical operation, this is handling an error | |
765 // condition so it is OK to look up the entry again. | |
766 int rv = DoomEntry(key, NULL); | |
767 DCHECK_EQ(OK, rv); | |
768 } | |
769 | |
770 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) { | |
771 // Need to abandon the ActiveEntry, but any transaction attached to the entry | |
772 // should not be impacted. Dooming an entry only means that it will no | |
773 // longer be returned by FindActiveEntry (and it will also be destroyed once | |
774 // all consumers are finished with the entry). | |
775 ActiveEntriesMap::iterator it = active_entries_.find(key); | |
776 if (it == active_entries_.end()) { | |
777 DCHECK(trans); | |
778 return AsyncDoomEntry(key, trans); | |
779 } | |
780 | |
781 ActiveEntry* entry = it->second; | |
782 active_entries_.erase(it); | |
783 | |
784 // We keep track of doomed entries so that we can ensure that they are | |
785 // cleaned up properly when the cache is destroyed. | |
786 doomed_entries_.insert(entry); | |
787 | |
788 entry->disk_entry->Doom(); | |
789 entry->doomed = true; | |
790 | |
791 DCHECK(entry->writer || !entry->readers.empty() || | |
792 entry->will_process_pending_queue); | |
793 return OK; | |
794 } | |
795 | |
796 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) { | |
797 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL); | |
798 PendingOp* pending_op = GetPendingOp(key); | |
799 if (pending_op->writer) { | |
800 pending_op->pending_queue.push_back(item); | |
801 return ERR_IO_PENDING; | |
802 } | |
803 | |
804 DCHECK(pending_op->pending_queue.empty()); | |
805 | |
806 pending_op->writer = item; | |
807 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, | |
808 GetWeakPtr(), pending_op); | |
809 | |
810 int rv = disk_cache_->DoomEntry(key, pending_op->callback); | |
811 if (rv != ERR_IO_PENDING) { | |
812 item->ClearTransaction(); | |
813 pending_op->callback.Run(rv); | |
814 } | |
815 | |
816 return rv; | |
817 } | |
818 | |
819 void HttpCache::DoomMainEntryForUrl(const GURL& url) { | |
820 if (!disk_cache_) | |
821 return; | |
822 | |
823 HttpRequestInfo temp_info; | |
824 temp_info.url = url; | |
825 temp_info.method = "GET"; | |
826 std::string key = GenerateCacheKey(&temp_info); | |
827 | |
828 // Defer to DoomEntry if there is an active entry, otherwise call | |
829 // AsyncDoomEntry without triggering a callback. | |
830 if (active_entries_.count(key)) | |
831 DoomEntry(key, NULL); | |
832 else | |
833 AsyncDoomEntry(key, NULL); | |
834 } | |
835 | |
836 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) { | |
837 DCHECK(entry->doomed); | |
838 DCHECK(!entry->writer); | |
839 DCHECK(entry->readers.empty()); | |
840 DCHECK(entry->pending_queue.empty()); | |
841 | |
842 ActiveEntriesSet::iterator it = doomed_entries_.find(entry); | |
843 DCHECK(it != doomed_entries_.end()); | |
844 doomed_entries_.erase(it); | |
845 | |
846 delete entry; | |
847 } | |
848 | |
849 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { | |
850 ActiveEntriesMap::const_iterator it = active_entries_.find(key); | |
851 return it != active_entries_.end() ? it->second : NULL; | |
852 } | |
853 | |
854 HttpCache::ActiveEntry* HttpCache::ActivateEntry( | |
855 disk_cache::Entry* disk_entry) { | |
856 DCHECK(!FindActiveEntry(disk_entry->GetKey())); | |
857 ActiveEntry* entry = new ActiveEntry(disk_entry); | |
858 active_entries_[disk_entry->GetKey()] = entry; | |
859 return entry; | |
860 } | |
861 | |
862 void HttpCache::DeactivateEntry(ActiveEntry* entry) { | |
863 DCHECK(!entry->will_process_pending_queue); | |
864 DCHECK(!entry->doomed); | |
865 DCHECK(!entry->writer); | |
866 DCHECK(entry->disk_entry); | |
867 DCHECK(entry->readers.empty()); | |
868 DCHECK(entry->pending_queue.empty()); | |
869 | |
870 std::string key = entry->disk_entry->GetKey(); | |
871 if (key.empty()) | |
872 return SlowDeactivateEntry(entry); | |
873 | |
874 ActiveEntriesMap::iterator it = active_entries_.find(key); | |
875 DCHECK(it != active_entries_.end()); | |
876 DCHECK(it->second == entry); | |
877 | |
878 active_entries_.erase(it); | |
879 delete entry; | |
880 } | |
881 | |
882 // We don't know this entry's key so we have to find it without it. | |
883 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) { | |
884 for (ActiveEntriesMap::iterator it = active_entries_.begin(); | |
885 it != active_entries_.end(); ++it) { | |
886 if (it->second == entry) { | |
887 active_entries_.erase(it); | |
888 delete entry; | |
889 break; | |
890 } | |
891 } | |
892 } | |
893 | |
894 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) { | |
895 DCHECK(!FindActiveEntry(key)); | |
896 | |
897 PendingOpsMap::const_iterator it = pending_ops_.find(key); | |
898 if (it != pending_ops_.end()) | |
899 return it->second; | |
900 | |
901 PendingOp* operation = new PendingOp(); | |
902 pending_ops_[key] = operation; | |
903 return operation; | |
904 } | |
905 | |
906 void HttpCache::DeletePendingOp(PendingOp* pending_op) { | |
907 std::string key; | |
908 if (pending_op->disk_entry) | |
909 key = pending_op->disk_entry->GetKey(); | |
910 | |
911 if (!key.empty()) { | |
912 PendingOpsMap::iterator it = pending_ops_.find(key); | |
913 DCHECK(it != pending_ops_.end()); | |
914 pending_ops_.erase(it); | |
915 } else { | |
916 for (PendingOpsMap::iterator it = pending_ops_.begin(); | |
917 it != pending_ops_.end(); ++it) { | |
918 if (it->second == pending_op) { | |
919 pending_ops_.erase(it); | |
920 break; | |
921 } | |
922 } | |
923 } | |
924 DCHECK(pending_op->pending_queue.empty()); | |
925 | |
926 delete pending_op; | |
927 } | |
928 | |
929 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry, | |
930 Transaction* trans) { | |
931 ActiveEntry* active_entry = FindActiveEntry(key); | |
932 if (active_entry) { | |
933 *entry = active_entry; | |
934 return OK; | |
935 } | |
936 | |
937 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry); | |
938 PendingOp* pending_op = GetPendingOp(key); | |
939 if (pending_op->writer) { | |
940 pending_op->pending_queue.push_back(item); | |
941 return ERR_IO_PENDING; | |
942 } | |
943 | |
944 DCHECK(pending_op->pending_queue.empty()); | |
945 | |
946 pending_op->writer = item; | |
947 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, | |
948 GetWeakPtr(), pending_op); | |
949 | |
950 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry), | |
951 pending_op->callback); | |
952 if (rv != ERR_IO_PENDING) { | |
953 item->ClearTransaction(); | |
954 pending_op->callback.Run(rv); | |
955 } | |
956 | |
957 return rv; | |
958 } | |
959 | |
960 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry, | |
961 Transaction* trans) { | |
962 if (FindActiveEntry(key)) { | |
963 return ERR_CACHE_RACE; | |
964 } | |
965 | |
966 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry); | |
967 PendingOp* pending_op = GetPendingOp(key); | |
968 if (pending_op->writer) { | |
969 pending_op->pending_queue.push_back(item); | |
970 return ERR_IO_PENDING; | |
971 } | |
972 | |
973 DCHECK(pending_op->pending_queue.empty()); | |
974 | |
975 pending_op->writer = item; | |
976 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, | |
977 GetWeakPtr(), pending_op); | |
978 | |
979 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry), | |
980 pending_op->callback); | |
981 if (rv != ERR_IO_PENDING) { | |
982 item->ClearTransaction(); | |
983 pending_op->callback.Run(rv); | |
984 } | |
985 | |
986 return rv; | |
987 } | |
988 | |
989 void HttpCache::DestroyEntry(ActiveEntry* entry) { | |
990 if (entry->doomed) { | |
991 FinalizeDoomedEntry(entry); | |
992 } else { | |
993 DeactivateEntry(entry); | |
994 } | |
995 } | |
996 | |
997 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) { | |
998 DCHECK(entry); | |
999 DCHECK(entry->disk_entry); | |
1000 | |
1001 // We implement a basic reader/writer lock for the disk cache entry. If | |
1002 // there is already a writer, then everyone has to wait for the writer to | |
1003 // finish before they can access the cache entry. There can be multiple | |
1004 // readers. | |
1005 // | |
1006 // NOTE: If the transaction can only write, then the entry should not be in | |
1007 // use (since any existing entry should have already been doomed). | |
1008 | |
1009 if (entry->writer || entry->will_process_pending_queue) { | |
1010 entry->pending_queue.push_back(trans); | |
1011 return ERR_IO_PENDING; | |
1012 } | |
1013 | |
1014 if (trans->mode() & Transaction::WRITE) { | |
1015 // transaction needs exclusive access to the entry | |
1016 if (entry->readers.empty()) { | |
1017 entry->writer = trans; | |
1018 } else { | |
1019 entry->pending_queue.push_back(trans); | |
1020 return ERR_IO_PENDING; | |
1021 } | |
1022 } else { | |
1023 // transaction needs read access to the entry | |
1024 entry->readers.push_back(trans); | |
1025 } | |
1026 | |
1027 // We do this before calling EntryAvailable to force any further calls to | |
1028 // AddTransactionToEntry to add their transaction to the pending queue, which | |
1029 // ensures FIFO ordering. | |
1030 if (!entry->writer && !entry->pending_queue.empty()) | |
1031 ProcessPendingQueue(entry); | |
1032 | |
1033 return OK; | |
1034 } | |
1035 | |
1036 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans, | |
1037 bool cancel) { | |
1038 // If we already posted a task to move on to the next transaction and this was | |
1039 // the writer, there is nothing to cancel. | |
1040 if (entry->will_process_pending_queue && entry->readers.empty()) | |
1041 return; | |
1042 | |
1043 if (entry->writer) { | |
1044 DCHECK(trans == entry->writer); | |
1045 | |
1046 // Assume there was a failure. | |
1047 bool success = false; | |
1048 if (cancel) { | |
1049 DCHECK(entry->disk_entry); | |
1050 // This is a successful operation in the sense that we want to keep the | |
1051 // entry. | |
1052 success = trans->AddTruncatedFlag(); | |
1053 // The previous operation may have deleted the entry. | |
1054 if (!trans->entry()) | |
1055 return; | |
1056 } | |
1057 DoneWritingToEntry(entry, success); | |
1058 } else { | |
1059 DoneReadingFromEntry(entry, trans); | |
1060 } | |
1061 } | |
1062 | |
1063 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) { | |
1064 DCHECK(entry->readers.empty()); | |
1065 | |
1066 entry->writer = NULL; | |
1067 | |
1068 if (success) { | |
1069 ProcessPendingQueue(entry); | |
1070 } else { | |
1071 DCHECK(!entry->will_process_pending_queue); | |
1072 | |
1073 // We failed to create this entry. | |
1074 TransactionList pending_queue; | |
1075 pending_queue.swap(entry->pending_queue); | |
1076 | |
1077 entry->disk_entry->Doom(); | |
1078 DestroyEntry(entry); | |
1079 | |
1080 // We need to do something about these pending entries, which now need to | |
1081 // be added to a new entry. | |
1082 while (!pending_queue.empty()) { | |
1083 // ERR_CACHE_RACE causes the transaction to restart the whole process. | |
1084 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE); | |
1085 pending_queue.pop_front(); | |
1086 } | |
1087 } | |
1088 } | |
1089 | |
1090 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) { | |
1091 DCHECK(!entry->writer); | |
1092 | |
1093 TransactionList::iterator it = | |
1094 std::find(entry->readers.begin(), entry->readers.end(), trans); | |
1095 DCHECK(it != entry->readers.end()); | |
1096 | |
1097 entry->readers.erase(it); | |
1098 | |
1099 ProcessPendingQueue(entry); | |
1100 } | |
1101 | |
1102 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) { | |
1103 DCHECK(entry->writer); | |
1104 DCHECK(entry->writer->mode() == Transaction::READ_WRITE); | |
1105 DCHECK(entry->readers.empty()); | |
1106 | |
1107 Transaction* trans = entry->writer; | |
1108 | |
1109 entry->writer = NULL; | |
1110 entry->readers.push_back(trans); | |
1111 | |
1112 ProcessPendingQueue(entry); | |
1113 } | |
1114 | |
1115 LoadState HttpCache::GetLoadStateForPendingTransaction( | |
1116 const Transaction* trans) { | |
1117 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key()); | |
1118 if (i == active_entries_.end()) { | |
1119 // If this is really a pending transaction, and it is not part of | |
1120 // active_entries_, we should be creating the backend or the entry. | |
1121 return LOAD_STATE_WAITING_FOR_CACHE; | |
1122 } | |
1123 | |
1124 Transaction* writer = i->second->writer; | |
1125 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE; | |
1126 } | |
1127 | |
1128 void HttpCache::RemovePendingTransaction(Transaction* trans) { | |
1129 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key()); | |
1130 bool found = false; | |
1131 if (i != active_entries_.end()) | |
1132 found = RemovePendingTransactionFromEntry(i->second, trans); | |
1133 | |
1134 if (found) | |
1135 return; | |
1136 | |
1137 if (building_backend_) { | |
1138 PendingOpsMap::const_iterator j = pending_ops_.find(std::string()); | |
1139 if (j != pending_ops_.end()) | |
1140 found = RemovePendingTransactionFromPendingOp(j->second, trans); | |
1141 | |
1142 if (found) | |
1143 return; | |
1144 } | |
1145 | |
1146 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key()); | |
1147 if (j != pending_ops_.end()) | |
1148 found = RemovePendingTransactionFromPendingOp(j->second, trans); | |
1149 | |
1150 if (found) | |
1151 return; | |
1152 | |
1153 ActiveEntriesSet::iterator k = doomed_entries_.begin(); | |
1154 for (; k != doomed_entries_.end() && !found; ++k) | |
1155 found = RemovePendingTransactionFromEntry(*k, trans); | |
1156 | |
1157 DCHECK(found) << "Pending transaction not found"; | |
1158 } | |
1159 | |
1160 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry, | |
1161 Transaction* trans) { | |
1162 TransactionList& pending_queue = entry->pending_queue; | |
1163 | |
1164 TransactionList::iterator j = | |
1165 find(pending_queue.begin(), pending_queue.end(), trans); | |
1166 if (j == pending_queue.end()) | |
1167 return false; | |
1168 | |
1169 pending_queue.erase(j); | |
1170 return true; | |
1171 } | |
1172 | |
1173 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op, | |
1174 Transaction* trans) { | |
1175 if (pending_op->writer->Matches(trans)) { | |
1176 pending_op->writer->ClearTransaction(); | |
1177 pending_op->writer->ClearEntry(); | |
1178 return true; | |
1179 } | |
1180 WorkItemList& pending_queue = pending_op->pending_queue; | |
1181 | |
1182 WorkItemList::iterator it = pending_queue.begin(); | |
1183 for (; it != pending_queue.end(); ++it) { | |
1184 if ((*it)->Matches(trans)) { | |
1185 delete *it; | |
1186 pending_queue.erase(it); | |
1187 return true; | |
1188 } | |
1189 } | |
1190 return false; | |
1191 } | |
1192 | |
1193 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) { | |
1194 if (session && | |
1195 !session->quic_stream_factory()->has_quic_server_info_factory()) { | |
1196 DCHECK(!quic_server_info_factory_); | |
1197 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this)); | |
1198 session->quic_stream_factory()->set_quic_server_info_factory( | |
1199 quic_server_info_factory_.get()); | |
1200 } | |
1201 } | |
1202 | |
1203 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { | |
1204 // Multiple readers may finish with an entry at once, so we want to batch up | |
1205 // calls to OnProcessPendingQueue. This flag also tells us that we should | |
1206 // not delete the entry before OnProcessPendingQueue runs. | |
1207 if (entry->will_process_pending_queue) | |
1208 return; | |
1209 entry->will_process_pending_queue = true; | |
1210 | |
1211 base::MessageLoop::current()->PostTask( | |
1212 FROM_HERE, | |
1213 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry)); | |
1214 } | |
1215 | |
1216 void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request, | |
1217 const BoundNetLog& net_log) { | |
1218 DCHECK(use_stale_while_revalidate_); | |
1219 std::string key = GenerateCacheKey(&original_request); | |
1220 AsyncValidation* async_validation = | |
1221 new AsyncValidation(original_request, this); | |
1222 typedef AsyncValidationMap::value_type AsyncValidationKeyValue; | |
1223 bool insert_ok = | |
1224 async_validations_.insert(AsyncValidationKeyValue(key, async_validation)) | |
1225 .second; | |
1226 if (!insert_ok) { | |
1227 DVLOG(1) << "Harmless race condition detected on URL " | |
1228 << original_request.url << "; discarding redundant revalidation."; | |
1229 delete async_validation; | |
1230 return; | |
1231 } | |
1232 HttpNetworkSession* network_session = GetSession(); | |
1233 NetworkDelegate* network_delegate = NULL; | |
1234 if (network_session) | |
1235 network_delegate = network_session->network_delegate(); | |
1236 scoped_ptr<HttpTransaction> transaction; | |
1237 CreateTransaction(IDLE, &transaction); | |
1238 scoped_ptr<Transaction> downcast_transaction( | |
1239 static_cast<Transaction*>(transaction.release())); | |
1240 async_validation->Start( | |
1241 net_log, downcast_transaction.Pass(), network_delegate); | |
1242 // |async_validation| may have been deleted here. | |
1243 } | |
1244 | |
1245 void HttpCache::DeleteAsyncValidation(const std::string& url) { | |
1246 AsyncValidationMap::iterator it = async_validations_.find(url); | |
1247 CHECK(it != async_validations_.end()); // security-critical invariant | |
1248 AsyncValidation* async_validation = it->second; | |
1249 async_validations_.erase(it); | |
1250 delete async_validation; | |
1251 } | |
1252 | |
1253 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { | |
1254 entry->will_process_pending_queue = false; | |
1255 DCHECK(!entry->writer); | |
1256 | |
1257 // If no one is interested in this entry, then we can deactivate it. | |
1258 if (entry->pending_queue.empty()) { | |
1259 if (entry->readers.empty()) | |
1260 DestroyEntry(entry); | |
1261 return; | |
1262 } | |
1263 | |
1264 // Promote next transaction from the pending queue. | |
1265 Transaction* next = entry->pending_queue.front(); | |
1266 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty()) | |
1267 return; // Have to wait. | |
1268 | |
1269 entry->pending_queue.erase(entry->pending_queue.begin()); | |
1270 | |
1271 int rv = AddTransactionToEntry(entry, next); | |
1272 if (rv != ERR_IO_PENDING) { | |
1273 next->io_callback().Run(rv); | |
1274 } | |
1275 } | |
1276 | |
1277 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) { | |
1278 WorkItemOperation op = pending_op->writer->operation(); | |
1279 | |
1280 // Completing the creation of the backend is simpler than the other cases. | |
1281 if (op == WI_CREATE_BACKEND) | |
1282 return OnBackendCreated(result, pending_op); | |
1283 | |
1284 scoped_ptr<WorkItem> item(pending_op->writer); | |
1285 bool fail_requests = false; | |
1286 | |
1287 ActiveEntry* entry = NULL; | |
1288 std::string key; | |
1289 if (result == OK) { | |
1290 if (op == WI_DOOM_ENTRY) { | |
1291 // Anything after a Doom has to be restarted. | |
1292 fail_requests = true; | |
1293 } else if (item->IsValid()) { | |
1294 key = pending_op->disk_entry->GetKey(); | |
1295 entry = ActivateEntry(pending_op->disk_entry); | |
1296 } else { | |
1297 // The writer transaction is gone. | |
1298 if (op == WI_CREATE_ENTRY) | |
1299 pending_op->disk_entry->Doom(); | |
1300 pending_op->disk_entry->Close(); | |
1301 pending_op->disk_entry = NULL; | |
1302 fail_requests = true; | |
1303 } | |
1304 } | |
1305 | |
1306 // We are about to notify a bunch of transactions, and they may decide to | |
1307 // re-issue a request (or send a different one). If we don't delete | |
1308 // pending_op, the new request will be appended to the end of the list, and | |
1309 // we'll see it again from this point before it has a chance to complete (and | |
1310 // we'll be messing out the request order). The down side is that if for some | |
1311 // reason notifying request A ends up cancelling request B (for the same key), | |
1312 // we won't find request B anywhere (because it would be in a local variable | |
1313 // here) and that's bad. If there is a chance for that to happen, we'll have | |
1314 // to move the callback used to be a CancelableCallback. By the way, for this | |
1315 // to happen the action (to cancel B) has to be synchronous to the | |
1316 // notification for request A. | |
1317 WorkItemList pending_items; | |
1318 pending_items.swap(pending_op->pending_queue); | |
1319 DeletePendingOp(pending_op); | |
1320 | |
1321 item->NotifyTransaction(result, entry); | |
1322 | |
1323 while (!pending_items.empty()) { | |
1324 item.reset(pending_items.front()); | |
1325 pending_items.pop_front(); | |
1326 | |
1327 if (item->operation() == WI_DOOM_ENTRY) { | |
1328 // A queued doom request is always a race. | |
1329 fail_requests = true; | |
1330 } else if (result == OK) { | |
1331 entry = FindActiveEntry(key); | |
1332 if (!entry) | |
1333 fail_requests = true; | |
1334 } | |
1335 | |
1336 if (fail_requests) { | |
1337 item->NotifyTransaction(ERR_CACHE_RACE, NULL); | |
1338 continue; | |
1339 } | |
1340 | |
1341 if (item->operation() == WI_CREATE_ENTRY) { | |
1342 if (result == OK) { | |
1343 // A second Create request, but the first request succeeded. | |
1344 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL); | |
1345 } else { | |
1346 if (op != WI_CREATE_ENTRY) { | |
1347 // Failed Open followed by a Create. | |
1348 item->NotifyTransaction(ERR_CACHE_RACE, NULL); | |
1349 fail_requests = true; | |
1350 } else { | |
1351 item->NotifyTransaction(result, entry); | |
1352 } | |
1353 } | |
1354 } else { | |
1355 if (op == WI_CREATE_ENTRY && result != OK) { | |
1356 // Failed Create followed by an Open. | |
1357 item->NotifyTransaction(ERR_CACHE_RACE, NULL); | |
1358 fail_requests = true; | |
1359 } else { | |
1360 item->NotifyTransaction(result, entry); | |
1361 } | |
1362 } | |
1363 } | |
1364 } | |
1365 | |
1366 // static | |
1367 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache, | |
1368 PendingOp* pending_op, | |
1369 int rv) { | |
1370 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1371 tracked_objects::ScopedTracker tracking_profile( | |
1372 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1373 "422516 HttpCache::OnPendingOpComplete")); | |
1374 | |
1375 if (cache.get()) { | |
1376 cache->OnIOComplete(rv, pending_op); | |
1377 } else { | |
1378 // The callback was cancelled so we should delete the pending_op that | |
1379 // was used with this callback. | |
1380 delete pending_op; | |
1381 } | |
1382 } | |
1383 | |
1384 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) { | |
1385 scoped_ptr<WorkItem> item(pending_op->writer); | |
1386 WorkItemOperation op = item->operation(); | |
1387 DCHECK_EQ(WI_CREATE_BACKEND, op); | |
1388 | |
1389 // We don't need the callback anymore. | |
1390 pending_op->callback.Reset(); | |
1391 | |
1392 if (backend_factory_.get()) { | |
1393 // We may end up calling OnBackendCreated multiple times if we have pending | |
1394 // work items. The first call saves the backend and releases the factory, | |
1395 // and the last call clears building_backend_. | |
1396 backend_factory_.reset(); // Reclaim memory. | |
1397 if (result == OK) { | |
1398 disk_cache_ = pending_op->backend.Pass(); | |
1399 if (UseCertCache()) | |
1400 cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get())); | |
1401 } | |
1402 } | |
1403 | |
1404 if (!pending_op->pending_queue.empty()) { | |
1405 WorkItem* pending_item = pending_op->pending_queue.front(); | |
1406 pending_op->pending_queue.pop_front(); | |
1407 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation()); | |
1408 | |
1409 // We want to process a single callback at a time, because the cache may | |
1410 // go away from the callback. | |
1411 pending_op->writer = pending_item; | |
1412 | |
1413 base::MessageLoop::current()->PostTask( | |
1414 FROM_HERE, | |
1415 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(), | |
1416 result, pending_op)); | |
1417 } else { | |
1418 building_backend_ = false; | |
1419 DeletePendingOp(pending_op); | |
1420 } | |
1421 | |
1422 // The cache may be gone when we return from the callback. | |
1423 if (!item->DoCallback(result, disk_cache_.get())) | |
1424 item->NotifyTransaction(result, NULL); | |
1425 } | |
1426 | |
1427 } // namespace net | |
OLD | NEW |