• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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 <utility>
8 
9 #include "base/compiler_specific.h"
10 #include "base/feature_list.h"
11 #include "base/files/file_util.h"
12 #include "base/format_macros.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback.h"
15 #include "base/functional/callback_helpers.h"
16 #include "base/location.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/memory/raw_ptr.h"
19 #include "base/memory/ref_counted.h"
20 #include "base/metrics/field_trial.h"
21 #include "base/metrics/histogram_macros.h"
22 #include "base/metrics/histogram_macros_local.h"
23 #include "base/pickle.h"
24 #include "base/ranges/algorithm.h"
25 #include "base/strings/strcat.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/task/single_thread_task_runner.h"
30 #include "base/time/default_clock.h"
31 #include "build/build_config.h"
32 #include "http_request_info.h"
33 #include "net/base/cache_type.h"
34 #include "net/base/features.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_anonymization_key.h"
39 #include "net/base/network_isolation_key.h"
40 #include "net/base/upload_data_stream.h"
41 #include "net/disk_cache/disk_cache.h"
42 #include "net/http/http_cache_transaction.h"
43 #include "net/http/http_cache_writers.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/log/net_log_with_source.h"
51 #include "net/quic/quic_server_info.h"
52 #include "third_party/abseil-cpp/absl/types/optional.h"
53 
54 #if BUILDFLAG(IS_POSIX)
55 #include <unistd.h>
56 #endif
57 
58 namespace net {
59 
60 namespace {
61 // True if any HTTP cache has been initialized.
62 bool g_init_cache = false;
63 
64 // True if split cache is enabled by default. Must be set before any HTTP cache
65 // has been initialized.
66 bool g_enable_split_cache = false;
67 }  // namespace
68 
69 const char HttpCache::kDoubleKeyPrefix[] = "_dk_";
70 const char HttpCache::kDoubleKeySeparator[] = " ";
71 const char HttpCache::kSubframeDocumentResourcePrefix[] = "s_";
72 
DefaultBackend(CacheType type,BackendType backend_type,scoped_refptr<disk_cache::BackendFileOperationsFactory> file_operations_factory,const base::FilePath & path,int max_bytes,bool hard_reset)73 HttpCache::DefaultBackend::DefaultBackend(
74     CacheType type,
75     BackendType backend_type,
76     scoped_refptr<disk_cache::BackendFileOperationsFactory>
77         file_operations_factory,
78     const base::FilePath& path,
79     int max_bytes,
80     bool hard_reset)
81     : type_(type),
82       backend_type_(backend_type),
83       file_operations_factory_(std::move(file_operations_factory)),
84       path_(path),
85       max_bytes_(max_bytes),
86       hard_reset_(hard_reset) {}
87 
88 HttpCache::DefaultBackend::~DefaultBackend() = default;
89 
90 // static
InMemory(int max_bytes)91 std::unique_ptr<HttpCache::BackendFactory> HttpCache::DefaultBackend::InMemory(
92     int max_bytes) {
93   return std::make_unique<DefaultBackend>(MEMORY_CACHE, CACHE_BACKEND_DEFAULT,
94                                           /*file_operations_factory=*/nullptr,
95                                           base::FilePath(), max_bytes, false);
96 }
97 
CreateBackend(NetLog * net_log,base::OnceCallback<void (disk_cache::BackendResult)> callback)98 disk_cache::BackendResult HttpCache::DefaultBackend::CreateBackend(
99     NetLog* net_log,
100     base::OnceCallback<void(disk_cache::BackendResult)> callback) {
101   DCHECK_GE(max_bytes_, 0);
102   disk_cache::ResetHandling reset_handling =
103       hard_reset_ ? disk_cache::ResetHandling::kReset
104                   : disk_cache::ResetHandling::kResetOnError;
105   LOCAL_HISTOGRAM_BOOLEAN("HttpCache.HardReset", hard_reset_);
106 #if BUILDFLAG(IS_ANDROID)
107   if (app_status_listener_getter_) {
108     return disk_cache::CreateCacheBackend(
109         type_, backend_type_, file_operations_factory_, path_, max_bytes_,
110         reset_handling, net_log, std::move(callback),
111         app_status_listener_getter_);
112   }
113 #endif
114   return disk_cache::CreateCacheBackend(
115       type_, backend_type_, file_operations_factory_, path_, max_bytes_,
116       reset_handling, net_log, std::move(callback));
117 }
118 
119 #if BUILDFLAG(IS_ANDROID)
SetAppStatusListenerGetter(disk_cache::ApplicationStatusListenerGetter app_status_listener_getter)120 void HttpCache::DefaultBackend::SetAppStatusListenerGetter(
121     disk_cache::ApplicationStatusListenerGetter app_status_listener_getter) {
122   app_status_listener_getter_ = std::move(app_status_listener_getter);
123 }
124 #endif
125 
126 //-----------------------------------------------------------------------------
127 
ActiveEntry(disk_cache::Entry * entry,bool opened_in)128 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry, bool opened_in)
129     : disk_entry(entry), opened(opened_in) {
130   DCHECK(disk_entry);
131 }
132 
133 HttpCache::ActiveEntry::~ActiveEntry() = default;
134 
HasNoTransactions()135 bool HttpCache::ActiveEntry::HasNoTransactions() {
136   return (!writers || writers->IsEmpty()) && readers.empty() &&
137          add_to_entry_queue.empty() && done_headers_queue.empty() &&
138          !headers_transaction;
139 }
140 
SafeToDestroy()141 bool HttpCache::ActiveEntry::SafeToDestroy() {
142   return HasNoTransactions() && !writers && !will_process_queued_transactions;
143 }
144 
TransactionInReaders(Transaction * transaction) const145 bool HttpCache::ActiveEntry::TransactionInReaders(
146     Transaction* transaction) const {
147   return readers.count(transaction) > 0;
148 }
149 
150 //-----------------------------------------------------------------------------
151 
152 // This structure keeps track of work items that are attempting to create or
153 // open cache entries or the backend itself.
154 struct HttpCache::PendingOp {
155   PendingOp() = default;
156   ~PendingOp() = default;
157 
158   raw_ptr<disk_cache::Entry, AcrossTasksDanglingUntriaged> entry = nullptr;
159   bool entry_opened = false;  // rather than created.
160 
161   std::unique_ptr<disk_cache::Backend> backend;
162   std::unique_ptr<WorkItem> writer;
163   // True if there is a posted OnPendingOpComplete() task that might delete
164   // |this| without removing it from |pending_ops_|.  Note that since
165   // OnPendingOpComplete() is static, it will not get cancelled when HttpCache
166   // is destroyed.
167   bool callback_will_delete = false;
168   WorkItemList pending_queue;
169 };
170 
171 //-----------------------------------------------------------------------------
172 
173 // A work item encapsulates a single request to the backend with all the
174 // information needed to complete that request.
175 class HttpCache::WorkItem {
176  public:
WorkItem(WorkItemOperation operation,Transaction * transaction,ActiveEntry ** entry)177   WorkItem(WorkItemOperation operation,
178            Transaction* transaction,
179            ActiveEntry** entry)
180       : operation_(operation), transaction_(transaction), entry_(entry) {}
WorkItem(WorkItemOperation operation,Transaction * transaction,CompletionOnceCallback callback)181   WorkItem(WorkItemOperation operation,
182            Transaction* transaction,
183            CompletionOnceCallback callback)
184       : operation_(operation),
185         transaction_(transaction),
186         entry_(nullptr),
187         callback_(std::move(callback)) {}
188   ~WorkItem() = default;
189 
190   // Calls back the transaction with the result of the operation.
NotifyTransaction(int result,ActiveEntry * entry)191   void NotifyTransaction(int result, ActiveEntry* entry) {
192     if (entry_) {
193       *entry_ = entry;
194     }
195     if (transaction_) {
196       transaction_->cache_io_callback().Run(result);
197     }
198   }
199 
200   // Notifies the caller about the operation completion. Returns true if the
201   // callback was invoked.
DoCallback(int result)202   bool DoCallback(int result) {
203     if (!callback_.is_null()) {
204       std::move(callback_).Run(result);
205       return true;
206     }
207     return false;
208   }
209 
operation()210   WorkItemOperation operation() { return operation_; }
ClearTransaction()211   void ClearTransaction() { transaction_ = nullptr; }
ClearEntry()212   void ClearEntry() { entry_ = nullptr; }
ClearCallback()213   void ClearCallback() { callback_.Reset(); }
Matches(Transaction * transaction) const214   bool Matches(Transaction* transaction) const {
215     return transaction == transaction_;
216   }
IsValid() const217   bool IsValid() const {
218     return transaction_ || entry_ || !callback_.is_null();
219   }
220 
221  private:
222   WorkItemOperation operation_;
223   raw_ptr<Transaction, DanglingUntriaged> transaction_;
224   raw_ptr<ActiveEntry*, DanglingUntriaged> entry_;
225   CompletionOnceCallback callback_;  // User callback.
226 };
227 
228 //-----------------------------------------------------------------------------
229 
HttpCache(std::unique_ptr<HttpTransactionFactory> network_layer,std::unique_ptr<BackendFactory> backend_factory)230 HttpCache::HttpCache(std::unique_ptr<HttpTransactionFactory> network_layer,
231                      std::unique_ptr<BackendFactory> backend_factory)
232     : net_log_(nullptr),
233       backend_factory_(std::move(backend_factory)),
234 
235       network_layer_(std::move(network_layer)),
236       clock_(base::DefaultClock::GetInstance()) {
237   g_init_cache = true;
238   HttpNetworkSession* session = network_layer_->GetSession();
239   // Session may be NULL in unittests.
240   // TODO(mmenke): Seems like tests could be changed to provide a session,
241   // rather than having logic only used in unit tests here.
242   if (!session) {
243     return;
244   }
245 
246   net_log_ = session->net_log();
247 }
248 
~HttpCache()249 HttpCache::~HttpCache() {
250   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
251   // Transactions should see an invalid cache after this point; otherwise they
252   // could see an inconsistent object (half destroyed).
253   weak_factory_.InvalidateWeakPtrs();
254 
255   // If we have any active entries remaining, then we need to deactivate them.
256   // We may have some pending tasks to process queued transactions ,but since
257   // those won't run (due to our destruction), we can simply ignore the
258   // corresponding flags.
259   while (!active_entries_.empty()) {
260     ActiveEntry* entry = active_entries_.begin()->second.get();
261     entry->will_process_queued_transactions = false;
262     entry->add_to_entry_queue.clear();
263     entry->readers.clear();
264     entry->done_headers_queue.clear();
265     entry->headers_transaction = nullptr;
266     entry->writers.reset();
267     DeactivateEntry(entry);
268   }
269 
270   doomed_entries_.clear();
271 
272   // Before deleting pending_ops_, we have to make sure that the disk cache is
273   // done with said operations, or it will attempt to use deleted data.
274   disk_cache_.reset();
275 
276   for (auto& pending_it : pending_ops_) {
277     // We are not notifying the transactions about the cache going away, even
278     // though they are waiting for a callback that will never fire.
279     PendingOp* pending_op = pending_it.second;
280     pending_op->writer.reset();
281     bool delete_pending_op = true;
282     if (building_backend_ && pending_op->callback_will_delete) {
283       // If we don't have a backend, when its construction finishes it will
284       // deliver the callbacks.
285       delete_pending_op = false;
286     }
287 
288     pending_op->pending_queue.clear();
289     if (delete_pending_op) {
290       delete pending_op;
291     }
292   }
293 }
294 
GetBackend(disk_cache::Backend ** backend,CompletionOnceCallback callback)295 int HttpCache::GetBackend(disk_cache::Backend** backend,
296                           CompletionOnceCallback callback) {
297   DCHECK(!callback.is_null());
298 
299   if (disk_cache_.get()) {
300     *backend = disk_cache_.get();
301     return OK;
302   }
303 
304   int rv =
305       CreateBackend(base::BindOnce(&HttpCache::ReportGetBackendResult,
306                                    GetWeakPtr(), backend, std::move(callback)));
307   if (rv != net::ERR_IO_PENDING) {
308     *backend = disk_cache_.get();
309   }
310   return rv;
311 }
312 
ReportGetBackendResult(disk_cache::Backend ** backend,CompletionOnceCallback callback,int net_error)313 void HttpCache::ReportGetBackendResult(disk_cache::Backend** backend,
314                                        CompletionOnceCallback callback,
315                                        int net_error) {
316   *backend = disk_cache_.get();
317   std::move(callback).Run(net_error);
318 }
319 
GetCurrentBackend() const320 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
321   return disk_cache_.get();
322 }
323 
324 // static
ParseResponseInfo(const char * data,int len,HttpResponseInfo * response_info,bool * response_truncated)325 bool HttpCache::ParseResponseInfo(const char* data,
326                                   int len,
327                                   HttpResponseInfo* response_info,
328                                   bool* response_truncated) {
329   base::Pickle pickle(data, len);
330   return response_info->InitFromPickle(pickle, response_truncated);
331 }
332 
CloseAllConnections(int net_error,const char * net_log_reason_utf8)333 void HttpCache::CloseAllConnections(int net_error,
334                                     const char* net_log_reason_utf8) {
335   HttpNetworkSession* session = GetSession();
336   if (session) {
337     session->CloseAllConnections(net_error, net_log_reason_utf8);
338   }
339 }
340 
CloseIdleConnections(const char * net_log_reason_utf8)341 void HttpCache::CloseIdleConnections(const char* net_log_reason_utf8) {
342   HttpNetworkSession* session = GetSession();
343   if (session) {
344     session->CloseIdleConnections(net_log_reason_utf8);
345   }
346 }
347 
OnExternalCacheHit(const GURL & url,const std::string & http_method,const NetworkIsolationKey & network_isolation_key,bool is_subframe_document_resource,bool used_credentials)348 void HttpCache::OnExternalCacheHit(
349     const GURL& url,
350     const std::string& http_method,
351     const NetworkIsolationKey& network_isolation_key,
352     bool is_subframe_document_resource,
353     bool used_credentials) {
354   if (!disk_cache_.get() || mode_ == DISABLE) {
355     return;
356   }
357 
358   if (IsSplitCacheEnabled() && network_isolation_key.IsTransient()) {
359     return;
360   }
361 
362   HttpRequestInfo request_info;
363   request_info.url = url;
364   request_info.method = http_method;
365   request_info.network_isolation_key = network_isolation_key;
366   request_info.network_anonymization_key =
367       net::NetworkAnonymizationKey::CreateFromNetworkIsolationKey(
368           network_isolation_key);
369 
370   request_info.is_subframe_document_resource = is_subframe_document_resource;
371   if (base::FeatureList::IsEnabled(features::kSplitCacheByIncludeCredentials)) {
372     if (!used_credentials) {
373       request_info.load_flags &= LOAD_DO_NOT_SAVE_COOKIES;
374     } else {
375       request_info.load_flags |= ~LOAD_DO_NOT_SAVE_COOKIES;
376     }
377   }
378 
379   std::string key = *GenerateCacheKeyForRequest(&request_info);
380   disk_cache_->OnExternalCacheHit(key);
381 }
382 
CreateTransaction(RequestPriority priority,std::unique_ptr<HttpTransaction> * transaction)383 int HttpCache::CreateTransaction(
384     RequestPriority priority,
385     std::unique_ptr<HttpTransaction>* transaction) {
386   // Do lazy initialization of disk cache if needed.
387   if (!disk_cache_.get()) {
388     // We don't care about the result.
389     CreateBackend(CompletionOnceCallback());
390   }
391 
392   auto new_transaction =
393       std::make_unique<HttpCache::Transaction>(priority, this);
394   if (bypass_lock_for_test_) {
395     new_transaction->BypassLockForTest();
396   }
397   if (bypass_lock_after_headers_for_test_) {
398     new_transaction->BypassLockAfterHeadersForTest();
399   }
400   if (fail_conditionalization_for_test_) {
401     new_transaction->FailConditionalizationForTest();
402   }
403 
404   *transaction = std::move(new_transaction);
405   return OK;
406 }
407 
GetCache()408 HttpCache* HttpCache::GetCache() {
409   return this;
410 }
411 
GetSession()412 HttpNetworkSession* HttpCache::GetSession() {
413   return network_layer_->GetSession();
414 }
415 
416 std::unique_ptr<HttpTransactionFactory>
SetHttpNetworkTransactionFactoryForTesting(std::unique_ptr<HttpTransactionFactory> new_network_layer)417 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
418     std::unique_ptr<HttpTransactionFactory> new_network_layer) {
419   std::unique_ptr<HttpTransactionFactory> old_network_layer(
420       std::move(network_layer_));
421   network_layer_ = std::move(new_network_layer);
422   return old_network_layer;
423 }
424 
425 // static
GetResourceURLFromHttpCacheKey(const std::string & key)426 std::string HttpCache::GetResourceURLFromHttpCacheKey(const std::string& key) {
427   // The key format is:
428   // credential_key/post_key/[isolation_key]url
429 
430   std::string::size_type pos = 0;
431   pos = key.find('/', pos) + 1;  // Consume credential_key/
432   pos = key.find('/', pos) + 1;  // Consume post_key/
433 
434   // It is a good idea to make this function tolerate invalid input. This can
435   // happen because of disk corruption.
436   if (pos == std::string::npos) {
437     return "";
438   }
439 
440   // Consume [isolation_key].
441   // Search the key to see whether it begins with |kDoubleKeyPrefix|. If so,
442   // then the entry was double-keyed.
443   if (pos == key.find(kDoubleKeyPrefix, pos)) {
444     // Find the rightmost occurrence of |kDoubleKeySeparator|, as when both
445     // the top-frame origin and the initiator are added to the key, there will
446     // be two occurrences of |kDoubleKeySeparator|.  When the cache entry is
447     // originally written to disk, GenerateCacheKey method calls
448     // HttpUtil::SpecForRequest method, which has a DCHECK to ensure that
449     // the original resource url is valid, and hence will not contain the
450     // unescaped whitespace of |kDoubleKeySeparator|.
451     pos = key.rfind(kDoubleKeySeparator);
452     DCHECK_NE(pos, std::string::npos);
453     pos += strlen(kDoubleKeySeparator);
454     DCHECK_LE(pos, key.size() - 1);
455   }
456   return key.substr(pos);
457 }
458 
459 // static
460 // Generate a key that can be used inside the cache.
GenerateCacheKey(const GURL & url,int load_flags,const NetworkIsolationKey & network_isolation_key,int64_t upload_data_identifier,bool is_subframe_document_resource)461 absl::optional<std::string> HttpCache::GenerateCacheKey(
462     const GURL& url,
463     int load_flags,
464     const NetworkIsolationKey& network_isolation_key,
465     int64_t upload_data_identifier,
466     bool is_subframe_document_resource) {
467   // The first character of the key may vary depending on whether or not sending
468   // credentials is permitted for this request. This only happens if the
469   // SplitCacheByIncludeCredentials feature is enabled.
470   const char credential_key = (base::FeatureList::IsEnabled(
471                                    features::kSplitCacheByIncludeCredentials) &&
472                                (load_flags & LOAD_DO_NOT_SAVE_COOKIES))
473                                   ? '0'
474                                   : '1';
475 
476   std::string isolation_key;
477   if (IsSplitCacheEnabled()) {
478     // Prepend the key with |kDoubleKeyPrefix| = "_dk_" to mark it as
479     // double-keyed (and makes it an invalid url so that it doesn't get
480     // confused with a single-keyed entry). Separate the origin and url
481     // with invalid whitespace character |kDoubleKeySeparator|.
482     if (network_isolation_key.IsTransient()) {
483       return absl::nullopt;
484     }
485     std::string subframe_document_resource_prefix =
486         is_subframe_document_resource ? kSubframeDocumentResourcePrefix : "";
487     isolation_key = base::StrCat(
488         {kDoubleKeyPrefix, subframe_document_resource_prefix,
489          *network_isolation_key.ToCacheKeyString(), kDoubleKeySeparator});
490   }
491 
492   // The key format is:
493   // credential_key/upload_data_identifier/[isolation_key]url
494 
495   // Strip out the reference, username, and password sections of the URL and
496   // concatenate with the credential_key, the post_key, and the network
497   // isolation key if we are splitting the cache.
498   return base::StringPrintf("%c/%" PRId64 "/%s%s", credential_key,
499                             upload_data_identifier, isolation_key.c_str(),
500                             HttpUtil::SpecForRequest(url).c_str());
501 }
502 
503 // static
GenerateCacheKeyForRequest(const HttpRequestInfo * request)504 absl::optional<std::string> HttpCache::GenerateCacheKeyForRequest(
505     const HttpRequestInfo* request) {
506   DCHECK(request);
507   const int64_t upload_data_identifier =
508       request->upload_data_stream ? request->upload_data_stream->identifier()
509                                   : int64_t(0);
510   return GenerateCacheKey(
511       request->url, request->load_flags, request->network_isolation_key,
512       upload_data_identifier, request->is_subframe_document_resource);
513 }
514 
515 // static
SplitCacheFeatureEnableByDefault()516 void HttpCache::SplitCacheFeatureEnableByDefault() {
517   CHECK(!g_enable_split_cache && !g_init_cache);
518   if (!base::FeatureList::GetInstance()->IsFeatureOverridden(
519           "SplitCacheByNetworkIsolationKey")) {
520     g_enable_split_cache = true;
521   }
522 }
523 
524 // static
IsSplitCacheEnabled()525 bool HttpCache::IsSplitCacheEnabled() {
526   return base::FeatureList::IsEnabled(
527              features::kSplitCacheByNetworkIsolationKey) ||
528          g_enable_split_cache;
529 }
530 
531 // static
ClearGlobalsForTesting()532 void HttpCache::ClearGlobalsForTesting() {
533   // Reset these so that unit tests can work.
534   g_init_cache = false;
535   g_enable_split_cache = false;
536 }
537 
538 //-----------------------------------------------------------------------------
539 
CreateAndSetWorkItem(ActiveEntry ** entry,Transaction * transaction,WorkItemOperation operation,PendingOp * pending_op)540 net::Error HttpCache::CreateAndSetWorkItem(ActiveEntry** entry,
541                                            Transaction* transaction,
542                                            WorkItemOperation operation,
543                                            PendingOp* pending_op) {
544   auto item = std::make_unique<WorkItem>(operation, transaction, entry);
545 
546   if (pending_op->writer) {
547     pending_op->pending_queue.push_back(std::move(item));
548     return ERR_IO_PENDING;
549   }
550 
551   DCHECK(pending_op->pending_queue.empty());
552 
553   pending_op->writer = std::move(item);
554   return OK;
555 }
556 
CreateBackend(CompletionOnceCallback callback)557 int HttpCache::CreateBackend(CompletionOnceCallback callback) {
558   DCHECK(!disk_cache_);
559 
560   if (!backend_factory_.get()) {
561     return ERR_FAILED;
562   }
563 
564   building_backend_ = true;
565 
566   const bool callback_is_null = callback.is_null();
567   std::unique_ptr<WorkItem> item = std::make_unique<WorkItem>(
568       WI_CREATE_BACKEND, nullptr, std::move(callback));
569 
570   // This is the only operation that we can do that is not related to any given
571   // entry, so we use an empty key for it.
572   PendingOp* pending_op = GetPendingOp(std::string());
573   if (pending_op->writer) {
574     if (!callback_is_null) {
575       pending_op->pending_queue.push_back(std::move(item));
576     }
577     return ERR_IO_PENDING;
578   }
579 
580   DCHECK(pending_op->pending_queue.empty());
581 
582   pending_op->writer = std::move(item);
583 
584   disk_cache::BackendResult result = backend_factory_->CreateBackend(
585       net_log_, base::BindOnce(&HttpCache::OnPendingBackendCreationOpComplete,
586                                GetWeakPtr(), pending_op));
587   if (result.net_error == ERR_IO_PENDING) {
588     pending_op->callback_will_delete = true;
589     return result.net_error;
590   }
591 
592   pending_op->writer->ClearCallback();
593   int rv = result.net_error;
594   OnPendingBackendCreationOpComplete(GetWeakPtr(), pending_op,
595                                      std::move(result));
596   return rv;
597 }
598 
GetBackendForTransaction(Transaction * transaction)599 int HttpCache::GetBackendForTransaction(Transaction* transaction) {
600   if (disk_cache_.get()) {
601     return OK;
602   }
603 
604   if (!building_backend_) {
605     return ERR_FAILED;
606   }
607 
608   std::unique_ptr<WorkItem> item = std::make_unique<WorkItem>(
609       WI_CREATE_BACKEND, transaction, CompletionOnceCallback());
610   PendingOp* pending_op = GetPendingOp(std::string());
611   DCHECK(pending_op->writer);
612   pending_op->pending_queue.push_back(std::move(item));
613   return ERR_IO_PENDING;
614 }
615 
DoomActiveEntry(const std::string & key)616 void HttpCache::DoomActiveEntry(const std::string& key) {
617   auto it = active_entries_.find(key);
618   if (it == active_entries_.end()) {
619     return;
620   }
621 
622   // This is not a performance critical operation, this is handling an error
623   // condition so it is OK to look up the entry again.
624   int rv = DoomEntry(key, nullptr);
625   DCHECK_EQ(OK, rv);
626 }
627 
DoomEntry(const std::string & key,Transaction * transaction)628 int HttpCache::DoomEntry(const std::string& key, Transaction* transaction) {
629   // Need to abandon the ActiveEntry, but any transaction attached to the entry
630   // should not be impacted.  Dooming an entry only means that it will no
631   // longer be returned by FindActiveEntry (and it will also be destroyed once
632   // all consumers are finished with the entry).
633   auto it = active_entries_.find(key);
634   if (it == active_entries_.end()) {
635     DCHECK(transaction);
636     return AsyncDoomEntry(key, transaction);
637   }
638 
639   std::unique_ptr<ActiveEntry> entry = std::move(it->second);
640   active_entries_.erase(it);
641 
642   // We keep track of doomed entries so that we can ensure that they are
643   // cleaned up properly when the cache is destroyed.
644   ActiveEntry* entry_ptr = entry.get();
645   DCHECK_EQ(0u, doomed_entries_.count(entry_ptr));
646   doomed_entries_[entry_ptr] = std::move(entry);
647 
648   entry_ptr->GetEntry()->Doom();
649   entry_ptr->doomed = true;
650 
651   DCHECK(!entry_ptr->SafeToDestroy());
652   return OK;
653 }
654 
AsyncDoomEntry(const std::string & key,Transaction * transaction)655 int HttpCache::AsyncDoomEntry(const std::string& key,
656                               Transaction* transaction) {
657   PendingOp* pending_op = GetPendingOp(key);
658   int rv =
659       CreateAndSetWorkItem(nullptr, transaction, WI_DOOM_ENTRY, pending_op);
660   if (rv != OK) {
661     return rv;
662   }
663 
664   net::RequestPriority priority =
665       transaction ? transaction->priority() : net::LOWEST;
666   rv = disk_cache_->DoomEntry(key, priority,
667                               base::BindOnce(&HttpCache::OnPendingOpComplete,
668                                              GetWeakPtr(), pending_op));
669   if (rv == ERR_IO_PENDING) {
670     pending_op->callback_will_delete = true;
671     return rv;
672   }
673 
674   pending_op->writer->ClearTransaction();
675   OnPendingOpComplete(GetWeakPtr(), pending_op, rv);
676   return rv;
677 }
678 
DoomMainEntryForUrl(const GURL & url,const NetworkIsolationKey & isolation_key,bool is_subframe_document_resource)679 void HttpCache::DoomMainEntryForUrl(const GURL& url,
680                                     const NetworkIsolationKey& isolation_key,
681                                     bool is_subframe_document_resource) {
682   if (!disk_cache_) {
683     return;
684   }
685 
686   if (IsSplitCacheEnabled() && isolation_key.IsTransient()) {
687     return;
688   }
689 
690   HttpRequestInfo temp_info;
691   temp_info.url = url;
692   temp_info.method = "GET";
693   temp_info.network_isolation_key = isolation_key;
694   temp_info.network_anonymization_key =
695       net::NetworkAnonymizationKey::CreateFromNetworkIsolationKey(
696           isolation_key);
697   temp_info.is_subframe_document_resource = is_subframe_document_resource;
698   std::string key = *GenerateCacheKeyForRequest(&temp_info);
699 
700   // Defer to DoomEntry if there is an active entry, otherwise call
701   // AsyncDoomEntry without triggering a callback.
702   if (active_entries_.count(key)) {
703     DoomEntry(key, nullptr);
704   } else {
705     AsyncDoomEntry(key, nullptr);
706   }
707 }
708 
FinalizeDoomedEntry(ActiveEntry * entry)709 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
710   DCHECK(entry->doomed);
711   DCHECK(entry->SafeToDestroy());
712 
713   auto it = doomed_entries_.find(entry);
714   DCHECK(it != doomed_entries_.end());
715   doomed_entries_.erase(it);
716 }
717 
FindActiveEntry(const std::string & key)718 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
719   auto it = active_entries_.find(key);
720   return it != active_entries_.end() ? it->second.get() : nullptr;
721 }
722 
ActivateEntry(disk_cache::Entry * disk_entry,bool opened)723 HttpCache::ActiveEntry* HttpCache::ActivateEntry(disk_cache::Entry* disk_entry,
724                                                  bool opened) {
725   DCHECK(!FindActiveEntry(disk_entry->GetKey()));
726   auto entry = std::make_unique<ActiveEntry>(disk_entry, opened);
727   ActiveEntry* entry_ptr = entry.get();
728   active_entries_[disk_entry->GetKey()] = std::move(entry);
729   return entry_ptr;
730 }
731 
DeactivateEntry(ActiveEntry * entry)732 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
733   DCHECK(!entry->doomed);
734   DCHECK(entry->SafeToDestroy());
735 
736   std::string key = entry->GetEntry()->GetKey();
737   if (key.empty()) {
738     return SlowDeactivateEntry(entry);
739   }
740 
741   auto it = active_entries_.find(key);
742   DCHECK(it != active_entries_.end());
743   DCHECK(it->second.get() == entry);
744 
745   active_entries_.erase(it);
746 }
747 
748 // We don't know this entry's key so we have to find it without it.
SlowDeactivateEntry(ActiveEntry * entry)749 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
750   for (auto it = active_entries_.begin(); it != active_entries_.end(); ++it) {
751     if (it->second.get() == entry) {
752       active_entries_.erase(it);
753       break;
754     }
755   }
756 }
757 
GetPendingOp(const std::string & key)758 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
759   DCHECK(!FindActiveEntry(key));
760 
761   auto it = pending_ops_.find(key);
762   if (it != pending_ops_.end()) {
763     return it->second;
764   }
765 
766   PendingOp* operation = new PendingOp();
767   pending_ops_[key] = operation;
768   return operation;
769 }
770 
DeletePendingOp(PendingOp * pending_op)771 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
772   std::string key;
773   if (pending_op->entry) {
774     key = pending_op->entry->GetKey();
775   }
776 
777   if (!key.empty()) {
778     auto it = pending_ops_.find(key);
779     DCHECK(it != pending_ops_.end());
780     pending_ops_.erase(it);
781   } else {
782     for (auto it = pending_ops_.begin(); it != pending_ops_.end(); ++it) {
783       if (it->second == pending_op) {
784         pending_ops_.erase(it);
785         break;
786       }
787     }
788   }
789   DCHECK(pending_op->pending_queue.empty());
790 
791   delete pending_op;
792 }
793 
OpenOrCreateEntry(const std::string & key,ActiveEntry ** entry,Transaction * transaction)794 int HttpCache::OpenOrCreateEntry(const std::string& key,
795                                  ActiveEntry** entry,
796                                  Transaction* transaction) {
797   DCHECK(!FindActiveEntry(key));
798 
799   PendingOp* pending_op = GetPendingOp(key);
800   int rv = CreateAndSetWorkItem(entry, transaction, WI_OPEN_OR_CREATE_ENTRY,
801                                 pending_op);
802   if (rv != OK) {
803     return rv;
804   }
805 
806   disk_cache::EntryResult entry_result = disk_cache_->OpenOrCreateEntry(
807       key, transaction->priority(),
808       base::BindOnce(&HttpCache::OnPendingCreationOpComplete, GetWeakPtr(),
809                      pending_op));
810   rv = entry_result.net_error();
811   if (rv == ERR_IO_PENDING) {
812     pending_op->callback_will_delete = true;
813     return ERR_IO_PENDING;
814   }
815 
816   pending_op->writer->ClearTransaction();
817   OnPendingCreationOpComplete(GetWeakPtr(), pending_op,
818                               std::move(entry_result));
819   return rv;
820 }
821 
OpenEntry(const std::string & key,ActiveEntry ** entry,Transaction * transaction)822 int HttpCache::OpenEntry(const std::string& key,
823                          ActiveEntry** entry,
824                          Transaction* transaction) {
825   DCHECK(!FindActiveEntry(key));
826 
827   PendingOp* pending_op = GetPendingOp(key);
828   int rv = CreateAndSetWorkItem(entry, transaction, WI_OPEN_ENTRY, pending_op);
829   if (rv != OK) {
830     return rv;
831   }
832 
833   disk_cache::EntryResult entry_result = disk_cache_->OpenEntry(
834       key, transaction->priority(),
835       base::BindOnce(&HttpCache::OnPendingCreationOpComplete, GetWeakPtr(),
836                      pending_op));
837   rv = entry_result.net_error();
838   if (rv == ERR_IO_PENDING) {
839     pending_op->callback_will_delete = true;
840     return ERR_IO_PENDING;
841   }
842 
843   pending_op->writer->ClearTransaction();
844   OnPendingCreationOpComplete(GetWeakPtr(), pending_op,
845                               std::move(entry_result));
846   return rv;
847 }
848 
CreateEntry(const std::string & key,ActiveEntry ** entry,Transaction * transaction)849 int HttpCache::CreateEntry(const std::string& key,
850                            ActiveEntry** entry,
851                            Transaction* transaction) {
852   if (FindActiveEntry(key)) {
853     return ERR_CACHE_RACE;
854   }
855 
856   PendingOp* pending_op = GetPendingOp(key);
857   int rv =
858       CreateAndSetWorkItem(entry, transaction, WI_CREATE_ENTRY, pending_op);
859   if (rv != OK) {
860     return rv;
861   }
862 
863   disk_cache::EntryResult entry_result = disk_cache_->CreateEntry(
864       key, transaction->priority(),
865       base::BindOnce(&HttpCache::OnPendingCreationOpComplete, GetWeakPtr(),
866                      pending_op));
867   rv = entry_result.net_error();
868   if (rv == ERR_IO_PENDING) {
869     pending_op->callback_will_delete = true;
870     return ERR_IO_PENDING;
871   }
872 
873   pending_op->writer->ClearTransaction();
874   OnPendingCreationOpComplete(GetWeakPtr(), pending_op,
875                               std::move(entry_result));
876   return rv;
877 }
878 
IsSafeToDestroyAndDestroyEntry(ActiveEntry * entry)879 bool HttpCache::IsSafeToDestroyAndDestroyEntry(ActiveEntry* entry) {
880   if (!entry->SafeToDestroy()) {
881     return false;
882   }
883   if (entry->doomed) {
884     FinalizeDoomedEntry(entry);
885   } else {
886     DeactivateEntry(entry);
887   }
888   return true;
889 }
890 
AddTransactionToEntry(ActiveEntry * entry,Transaction * transaction)891 int HttpCache::AddTransactionToEntry(ActiveEntry* entry,
892                                      Transaction* transaction) {
893   DCHECK(entry);
894   DCHECK(entry->GetEntry());
895   // Always add a new transaction to the queue to maintain FIFO order.
896   entry->add_to_entry_queue.push_back(transaction);
897   // Don't process the transaction if the lock timeout handling is being tested.
898   if (!bypass_lock_for_test_) {
899     ProcessQueuedTransactions(entry);
900   }
901   return ERR_IO_PENDING;
902 }
903 
DoneWithResponseHeaders(ActiveEntry * entry,Transaction * transaction,bool is_partial)904 int HttpCache::DoneWithResponseHeaders(ActiveEntry* entry,
905                                        Transaction* transaction,
906                                        bool is_partial) {
907   // If |transaction| is the current writer, do nothing. This can happen for
908   // range requests since they can go back to headers phase after starting to
909   // write.
910   if (entry->writers && entry->writers->HasTransaction(transaction)) {
911     DCHECK(is_partial && entry->writers->GetTransactionsCount() == 1);
912     return OK;
913   }
914 
915   DCHECK_EQ(entry->headers_transaction, transaction);
916 
917   entry->headers_transaction = nullptr;
918 
919   // If transaction is responsible for writing the response body, then do not go
920   // through done_headers_queue for performance benefit. (Also, in case of
921   // writer transaction, the consumer sometimes depend on synchronous behaviour
922   // e.g. while computing raw headers size. (crbug.com/711766))
923   if ((transaction->mode() & Transaction::WRITE) && !entry->writers &&
924       entry->readers.empty()) {
925     AddTransactionToWriters(entry, transaction,
926                             CanTransactionJoinExistingWriters(transaction));
927     ProcessQueuedTransactions(entry);
928     return OK;
929   }
930 
931   entry->done_headers_queue.push_back(transaction);
932   ProcessQueuedTransactions(entry);
933   return ERR_IO_PENDING;
934 }
935 
DoneWithEntry(ActiveEntry * entry,Transaction * transaction,bool entry_is_complete,bool is_partial)936 void HttpCache::DoneWithEntry(ActiveEntry* entry,
937                               Transaction* transaction,
938                               bool entry_is_complete,
939                               bool is_partial) {
940   bool is_mode_read_only = transaction->mode() == Transaction::READ;
941 
942   if (!entry_is_complete && !is_mode_read_only && is_partial) {
943     entry->GetEntry()->CancelSparseIO();
944   }
945 
946   // Transaction is waiting in the done_headers_queue.
947   auto it = base::ranges::find(entry->done_headers_queue, transaction);
948   if (it != entry->done_headers_queue.end()) {
949     entry->done_headers_queue.erase(it);
950 
951     // Restart other transactions if this transaction could have written
952     // response body.
953     if (!entry_is_complete && !is_mode_read_only) {
954       ProcessEntryFailure(entry);
955     }
956     return;
957   }
958 
959   // Transaction is removed in the headers phase.
960   if (transaction == entry->headers_transaction) {
961     entry->headers_transaction = nullptr;
962 
963     if (entry_is_complete || is_mode_read_only) {
964       ProcessQueuedTransactions(entry);
965     } else {
966       // Restart other transactions if this transaction could have written
967       // response body.
968       ProcessEntryFailure(entry);
969     }
970     return;
971   }
972 
973   // Transaction is removed in the writing phase.
974   if (entry->writers && entry->writers->HasTransaction(transaction)) {
975     entry->writers->RemoveTransaction(transaction,
976                                       entry_is_complete /* success */);
977     return;
978   }
979 
980   // Transaction is reading from the entry.
981   DCHECK(!entry->writers);
982   auto readers_it = entry->readers.find(transaction);
983   DCHECK(readers_it != entry->readers.end());
984   entry->readers.erase(readers_it);
985   ProcessQueuedTransactions(entry);
986 }
987 
WritersDoomEntryRestartTransactions(ActiveEntry * entry)988 void HttpCache::WritersDoomEntryRestartTransactions(ActiveEntry* entry) {
989   DCHECK(!entry->writers->IsEmpty());
990   ProcessEntryFailure(entry);
991 }
992 
WritersDoneWritingToEntry(ActiveEntry * entry,bool success,bool should_keep_entry,TransactionSet make_readers)993 void HttpCache::WritersDoneWritingToEntry(ActiveEntry* entry,
994                                           bool success,
995                                           bool should_keep_entry,
996                                           TransactionSet make_readers) {
997   // Impacts the queued transactions in one of the following ways:
998   // - restart them but do not doom the entry since entry can be saved in
999   // its truncated form.
1000   // - restart them and doom/destroy the entry since entry does not
1001   // have valid contents.
1002   // - let them continue by invoking their callback since entry is
1003   // successfully written.
1004   DCHECK(entry->writers);
1005   DCHECK(entry->writers->IsEmpty());
1006   DCHECK(success || make_readers.empty());
1007 
1008   if (!success && should_keep_entry) {
1009     // Restart already validated transactions so that they are able to read
1010     // the truncated status of the entry.
1011     RestartHeadersPhaseTransactions(entry);
1012     entry->writers.reset();
1013     IsSafeToDestroyAndDestroyEntry(entry);
1014     return;
1015   }
1016 
1017   if (success) {
1018     // Add any idle writers to readers.
1019     for (auto* reader : make_readers) {
1020       reader->WriteModeTransactionAboutToBecomeReader();
1021       entry->readers.insert(reader);
1022     }
1023     // Reset writers here so that WriteModeTransactionAboutToBecomeReader can
1024     // access the network transaction.
1025     entry->writers.reset();
1026     ProcessQueuedTransactions(entry);
1027   } else {
1028     entry->writers.reset();
1029     ProcessEntryFailure(entry);
1030   }
1031 }
1032 
DoomEntryValidationNoMatch(ActiveEntry * entry)1033 void HttpCache::DoomEntryValidationNoMatch(ActiveEntry* entry) {
1034   // Validating transaction received a non-matching response.
1035   DCHECK(entry->headers_transaction);
1036 
1037   entry->headers_transaction = nullptr;
1038   if (entry->SafeToDestroy()) {
1039     entry->GetEntry()->Doom();
1040     IsSafeToDestroyAndDestroyEntry(entry);
1041     return;
1042   }
1043 
1044   DoomActiveEntry(entry->GetEntry()->GetKey());
1045 
1046   // Restart only add_to_entry_queue transactions.
1047   // Post task here to avoid a race in creating the entry between |transaction|
1048   // and the add_to_entry_queue transactions. Reset the queued transaction's
1049   // cache pending state so that in case it's destructor is invoked, it's ok
1050   // for the transaction to not be found in this entry.
1051   for (auto* transaction : entry->add_to_entry_queue) {
1052     transaction->ResetCachePendingState();
1053     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1054         FROM_HERE,
1055         base::BindOnce(transaction->cache_io_callback(), net::ERR_CACHE_RACE));
1056   }
1057   entry->add_to_entry_queue.clear();
1058 }
1059 
RemoveAllQueuedTransactions(ActiveEntry * entry,TransactionList * list)1060 void HttpCache::RemoveAllQueuedTransactions(ActiveEntry* entry,
1061                                             TransactionList* list) {
1062   // Process done_headers_queue before add_to_entry_queue to maintain FIFO
1063   // order.
1064 
1065   for (auto* transaction : entry->done_headers_queue) {
1066     list->push_back(transaction);
1067   }
1068   entry->done_headers_queue.clear();
1069 
1070   for (auto* pending_transaction : entry->add_to_entry_queue) {
1071     list->push_back(pending_transaction);
1072   }
1073   entry->add_to_entry_queue.clear();
1074 }
1075 
ProcessEntryFailure(ActiveEntry * entry)1076 void HttpCache::ProcessEntryFailure(ActiveEntry* entry) {
1077   // The writer failed to completely write the response to
1078   // the cache.
1079 
1080   if (entry->headers_transaction) {
1081     RestartHeadersTransaction(entry);
1082   }
1083 
1084   TransactionList list;
1085   RemoveAllQueuedTransactions(entry, &list);
1086 
1087   if (entry->SafeToDestroy()) {
1088     entry->GetEntry()->Doom();
1089     IsSafeToDestroyAndDestroyEntry(entry);
1090   } else {
1091     DoomActiveEntry(entry->GetEntry()->GetKey());
1092   }
1093   // ERR_CACHE_RACE causes the transaction to restart the whole process.
1094   for (auto* queued_transaction : list) {
1095     queued_transaction->cache_io_callback().Run(net::ERR_CACHE_RACE);
1096   }
1097 }
1098 
RestartHeadersPhaseTransactions(ActiveEntry * entry)1099 void HttpCache::RestartHeadersPhaseTransactions(ActiveEntry* entry) {
1100   if (entry->headers_transaction) {
1101     RestartHeadersTransaction(entry);
1102   }
1103 
1104   auto it = entry->done_headers_queue.begin();
1105   while (it != entry->done_headers_queue.end()) {
1106     Transaction* done_headers_transaction = *it;
1107     it = entry->done_headers_queue.erase(it);
1108     done_headers_transaction->cache_io_callback().Run(net::ERR_CACHE_RACE);
1109   }
1110 }
1111 
RestartHeadersTransaction(ActiveEntry * entry)1112 void HttpCache::RestartHeadersTransaction(ActiveEntry* entry) {
1113   entry->headers_transaction->SetValidatingCannotProceed();
1114   entry->headers_transaction = nullptr;
1115 }
1116 
ProcessQueuedTransactions(ActiveEntry * entry)1117 void HttpCache::ProcessQueuedTransactions(ActiveEntry* entry) {
1118   // Multiple readers may finish with an entry at once, so we want to batch up
1119   // calls to OnProcessQueuedTransactions. This flag also tells us that we
1120   // should not delete the entry before OnProcessQueuedTransactions runs.
1121   if (entry->will_process_queued_transactions) {
1122     return;
1123   }
1124 
1125   entry->will_process_queued_transactions = true;
1126 
1127   // Entry should not be safe to destroy when bound to a posted task.
1128   CHECK(!entry->SafeToDestroy());
1129 
1130   // Post a task instead of invoking the io callback of another transaction here
1131   // to avoid re-entrancy.
1132   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1133       FROM_HERE,
1134       base::BindOnce(&HttpCache::OnProcessQueuedTransactions, GetWeakPtr(),
1135                      // Safe to bind the ActiveEntry pointer since `this` owns
1136                      // the ActiveEntry and will only destroy the ActiveEntry
1137                      // when the ActiveEntry's SafeToDestroy returns true.
1138                      //
1139                      // We are guaranteed that it's SafeToDestroy will always
1140                      // return false until this callback is invoked because
1141                      // SafeToDestroy will return false if
1142                      // will_process_queued_transactions is true. We've set
1143                      // entry->will_process_queued_transactions to true above
1144                      // and will only set it to false when this callback is run.
1145                      entry));
1146 }
1147 
ProcessAddToEntryQueue(ActiveEntry * entry)1148 void HttpCache::ProcessAddToEntryQueue(ActiveEntry* entry) {
1149   CHECK(!entry->add_to_entry_queue.empty());
1150   if (delay_add_transaction_to_entry_for_test_) {
1151     // Entry should not be safe to destroy when bound to a posted task.
1152     CHECK(!entry->SafeToDestroy());
1153 
1154     // Post a task to put the AddTransactionToEntry handling at the back of
1155     // the task queue. This allows other tasks (like network IO) to jump
1156     // ahead and simulate different callback ordering for testing.
1157     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1158         FROM_HERE,
1159         base::BindOnce(
1160             &HttpCache::ProcessAddToEntryQueueImpl, GetWeakPtr(),
1161             // Safe to bind the ActiveEntry pointer since `this` owns the
1162             // ActiveEntry and will only destroy the ActiveEntry when the
1163             // ActiveEntry's SafeToDestroy returns true.
1164             //
1165             // We are guaranteed that it's SafeToDestroy will always return
1166             // false until this callback is invoked because SafeToDestroy will
1167             // return false if entry->add_to_entry_queue.empty() is false. We
1168             // can only call this function when
1169             // entry->add_to_entry_queue.empty() is false.
1170             entry));
1171   } else {
1172     ProcessAddToEntryQueueImpl(entry);
1173   }
1174 }
1175 
ProcessAddToEntryQueueImpl(ActiveEntry * entry)1176 void HttpCache::ProcessAddToEntryQueueImpl(ActiveEntry* entry) {
1177   DCHECK(!entry->add_to_entry_queue.empty());
1178 
1179   // Note the entry may be new or may already have a response body written to
1180   // it. In both cases, a transaction needs to wait since only one transaction
1181   // can be in the headers phase at a time.
1182   if (entry->headers_transaction) {
1183     return;
1184   }
1185   Transaction* transaction = entry->add_to_entry_queue.front();
1186   entry->add_to_entry_queue.erase(entry->add_to_entry_queue.begin());
1187   entry->headers_transaction = transaction;
1188 
1189   transaction->cache_io_callback().Run(OK);
1190 }
1191 
CanTransactionJoinExistingWriters(Transaction * transaction)1192 HttpCache::ParallelWritingPattern HttpCache::CanTransactionJoinExistingWriters(
1193     Transaction* transaction) {
1194   if (transaction->method() != "GET") {
1195     return PARALLEL_WRITING_NOT_JOIN_METHOD_NOT_GET;
1196   }
1197   if (transaction->partial()) {
1198     return PARALLEL_WRITING_NOT_JOIN_RANGE;
1199   }
1200   if (transaction->mode() == Transaction::READ) {
1201     return PARALLEL_WRITING_NOT_JOIN_READ_ONLY;
1202   }
1203   if (transaction->GetResponseInfo()->headers &&
1204       transaction->GetResponseInfo()->headers->GetContentLength() >
1205           disk_cache_->MaxFileSize()) {
1206     return PARALLEL_WRITING_NOT_JOIN_TOO_BIG_FOR_CACHE;
1207   }
1208   return PARALLEL_WRITING_JOIN;
1209 }
1210 
ProcessDoneHeadersQueue(ActiveEntry * entry)1211 void HttpCache::ProcessDoneHeadersQueue(ActiveEntry* entry) {
1212   ParallelWritingPattern writers_pattern;
1213   DCHECK(!entry->writers || entry->writers->CanAddWriters(&writers_pattern));
1214   DCHECK(!entry->done_headers_queue.empty());
1215 
1216   Transaction* transaction = entry->done_headers_queue.front();
1217 
1218   ParallelWritingPattern parallel_writing_pattern =
1219       CanTransactionJoinExistingWriters(transaction);
1220   if (IsWritingInProgress(entry)) {
1221     if (parallel_writing_pattern != PARALLEL_WRITING_JOIN) {
1222       // TODO(shivanisha): Returning from here instead of checking the next
1223       // transaction in the queue because the FIFO order is maintained
1224       // throughout, until it becomes a reader or writer. May be at this point
1225       // the ordering is not important but that would be optimizing a rare
1226       // scenario where write mode transactions are insterspersed with read-only
1227       // transactions.
1228       return;
1229     }
1230     AddTransactionToWriters(entry, transaction, parallel_writing_pattern);
1231   } else {  // no writing in progress
1232     if (transaction->mode() & Transaction::WRITE) {
1233       if (transaction->partial()) {
1234         if (entry->readers.empty()) {
1235           AddTransactionToWriters(entry, transaction, parallel_writing_pattern);
1236         } else {
1237           return;
1238         }
1239       } else {
1240         // Add the transaction to readers since the response body should have
1241         // already been written. (If it was the first writer about to start
1242         // writing to the cache, it would have been added to writers in
1243         // DoneWithResponseHeaders, thus no writers here signify the response
1244         // was completely written).
1245         transaction->WriteModeTransactionAboutToBecomeReader();
1246         auto return_val = entry->readers.insert(transaction);
1247         DCHECK(return_val.second);
1248       }
1249     } else {  // mode READ
1250       auto return_val = entry->readers.insert(transaction);
1251       DCHECK(return_val.second);
1252     }
1253   }
1254 
1255   // Post another task to give a chance to more transactions to either join
1256   // readers or another transaction to start parallel validation.
1257   ProcessQueuedTransactions(entry);
1258 
1259   entry->done_headers_queue.erase(entry->done_headers_queue.begin());
1260   transaction->cache_io_callback().Run(OK);
1261 }
1262 
AddTransactionToWriters(ActiveEntry * entry,Transaction * transaction,ParallelWritingPattern parallel_writing_pattern)1263 void HttpCache::AddTransactionToWriters(
1264     ActiveEntry* entry,
1265     Transaction* transaction,
1266     ParallelWritingPattern parallel_writing_pattern) {
1267   if (!entry->writers) {
1268     entry->writers = std::make_unique<Writers>(this, entry);
1269   } else {
1270     ParallelWritingPattern writers_pattern;
1271     DCHECK(entry->writers->CanAddWriters(&writers_pattern));
1272     DCHECK_EQ(PARALLEL_WRITING_JOIN, writers_pattern);
1273   }
1274 
1275   Writers::TransactionInfo info(transaction->partial(),
1276                                 transaction->is_truncated(),
1277                                 *(transaction->GetResponseInfo()));
1278 
1279   entry->writers->AddTransaction(transaction, parallel_writing_pattern,
1280                                  transaction->priority(), info);
1281 }
1282 
CanTransactionWriteResponseHeaders(ActiveEntry * entry,Transaction * transaction,bool is_partial,bool is_match) const1283 bool HttpCache::CanTransactionWriteResponseHeaders(ActiveEntry* entry,
1284                                                    Transaction* transaction,
1285                                                    bool is_partial,
1286                                                    bool is_match) const {
1287   // If |transaction| is the current writer, do nothing. This can happen for
1288   // range requests since they can go back to headers phase after starting to
1289   // write.
1290   if (entry->writers && entry->writers->HasTransaction(transaction)) {
1291     DCHECK(is_partial);
1292     return true;
1293   }
1294 
1295   if (transaction != entry->headers_transaction) {
1296     return false;
1297   }
1298 
1299   if (!(transaction->mode() & Transaction::WRITE)) {
1300     return false;
1301   }
1302 
1303   // If its not a match then check if it is the transaction responsible for
1304   // writing the response body.
1305   if (!is_match) {
1306     return (!entry->writers || entry->writers->IsEmpty()) &&
1307            entry->done_headers_queue.empty() && entry->readers.empty();
1308   }
1309 
1310   return true;
1311 }
1312 
IsWritingInProgress(ActiveEntry * entry) const1313 bool HttpCache::IsWritingInProgress(ActiveEntry* entry) const {
1314   return entry->writers.get();
1315 }
1316 
GetLoadStateForPendingTransaction(const Transaction * transaction)1317 LoadState HttpCache::GetLoadStateForPendingTransaction(
1318     const Transaction* transaction) {
1319   auto i = active_entries_.find(transaction->key());
1320   if (i == active_entries_.end()) {
1321     // If this is really a pending transaction, and it is not part of
1322     // active_entries_, we should be creating the backend or the entry.
1323     return LOAD_STATE_WAITING_FOR_CACHE;
1324   }
1325 
1326   Writers* writers = i->second->writers.get();
1327   return !writers ? LOAD_STATE_WAITING_FOR_CACHE : writers->GetLoadState();
1328 }
1329 
RemovePendingTransaction(Transaction * transaction)1330 void HttpCache::RemovePendingTransaction(Transaction* transaction) {
1331   auto i = active_entries_.find(transaction->key());
1332   bool found = false;
1333   if (i != active_entries_.end()) {
1334     found = RemovePendingTransactionFromEntry(i->second.get(), transaction);
1335   }
1336 
1337   if (found) {
1338     return;
1339   }
1340 
1341   if (building_backend_) {
1342     auto j = pending_ops_.find(std::string());
1343     if (j != pending_ops_.end()) {
1344       found = RemovePendingTransactionFromPendingOp(j->second, transaction);
1345     }
1346 
1347     if (found) {
1348       return;
1349     }
1350   }
1351 
1352   auto j = pending_ops_.find(transaction->key());
1353   if (j != pending_ops_.end()) {
1354     found = RemovePendingTransactionFromPendingOp(j->second, transaction);
1355   }
1356 
1357   if (found) {
1358     return;
1359   }
1360 
1361   for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found;
1362        ++k) {
1363     found = RemovePendingTransactionFromEntry(k->first, transaction);
1364   }
1365 
1366   DCHECK(found) << "Pending transaction not found";
1367 }
1368 
RemovePendingTransactionFromEntry(ActiveEntry * entry,Transaction * transaction)1369 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
1370                                                   Transaction* transaction) {
1371   TransactionList& add_to_entry_queue = entry->add_to_entry_queue;
1372 
1373   auto j =
1374       find(add_to_entry_queue.begin(), add_to_entry_queue.end(), transaction);
1375   if (j == add_to_entry_queue.end()) {
1376     return false;
1377   }
1378 
1379   add_to_entry_queue.erase(j);
1380   return true;
1381 }
1382 
RemovePendingTransactionFromPendingOp(PendingOp * pending_op,Transaction * transaction)1383 bool HttpCache::RemovePendingTransactionFromPendingOp(
1384     PendingOp* pending_op,
1385     Transaction* transaction) {
1386   if (pending_op->writer->Matches(transaction)) {
1387     pending_op->writer->ClearTransaction();
1388     pending_op->writer->ClearEntry();
1389     return true;
1390   }
1391   WorkItemList& pending_queue = pending_op->pending_queue;
1392 
1393   for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) {
1394     if ((*it)->Matches(transaction)) {
1395       pending_queue.erase(it);
1396       return true;
1397     }
1398   }
1399   return false;
1400 }
1401 
OnProcessQueuedTransactions(ActiveEntry * entry)1402 void HttpCache::OnProcessQueuedTransactions(ActiveEntry* entry) {
1403   entry->will_process_queued_transactions = false;
1404 
1405   // Note that this function should only invoke one transaction's IO callback
1406   // since its possible for IO callbacks' consumers to destroy the cache/entry.
1407 
1408   // If no one is interested in this entry, then we can deactivate it.
1409   if (IsSafeToDestroyAndDestroyEntry(entry)) {
1410     return;
1411   }
1412 
1413   if (entry->done_headers_queue.empty() && entry->add_to_entry_queue.empty()) {
1414     return;
1415   }
1416 
1417   // To maintain FIFO order of transactions, done_headers_queue should be
1418   // checked for processing before add_to_entry_queue.
1419 
1420   // If another transaction is writing the response, let validated transactions
1421   // wait till the response is complete. If the response is not yet started, the
1422   // done_headers_queue transaction should start writing it.
1423   if (!entry->done_headers_queue.empty()) {
1424     ParallelWritingPattern unused_reason;
1425     if (!entry->writers || entry->writers->CanAddWriters(&unused_reason)) {
1426       ProcessDoneHeadersQueue(entry);
1427       return;
1428     }
1429   }
1430 
1431   if (!entry->add_to_entry_queue.empty()) {
1432     ProcessAddToEntryQueue(entry);
1433   }
1434 }
1435 
OnIOComplete(int result,PendingOp * pending_op)1436 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1437   WorkItemOperation op = pending_op->writer->operation();
1438 
1439   // Completing the creation of the backend is simpler than the other cases.
1440   if (op == WI_CREATE_BACKEND) {
1441     return OnBackendCreated(result, pending_op);
1442   }
1443 
1444   std::unique_ptr<WorkItem> item = std::move(pending_op->writer);
1445   bool try_restart_requests = false;
1446 
1447   ActiveEntry* entry = nullptr;
1448   std::string key;
1449   if (result == OK) {
1450     if (op == WI_DOOM_ENTRY) {
1451       // Anything after a Doom has to be restarted.
1452       try_restart_requests = true;
1453     } else if (item->IsValid()) {
1454       DCHECK(pending_op->entry);
1455       key = pending_op->entry->GetKey();
1456       entry = ActivateEntry(pending_op->entry, pending_op->entry_opened);
1457     } else {
1458       // The writer transaction is gone.
1459       if (!pending_op->entry_opened) {
1460         pending_op->entry->Doom();
1461       }
1462 
1463       pending_op->entry->Close();
1464       pending_op->entry = nullptr;
1465       try_restart_requests = true;
1466     }
1467   }
1468 
1469   // We are about to notify a bunch of transactions, and they may decide to
1470   // re-issue a request (or send a different one). If we don't delete
1471   // pending_op, the new request will be appended to the end of the list, and
1472   // we'll see it again from this point before it has a chance to complete (and
1473   // we'll be messing out the request order). The down side is that if for some
1474   // reason notifying request A ends up cancelling request B (for the same key),
1475   // we won't find request B anywhere (because it would be in a local variable
1476   // here) and that's bad. If there is a chance for that to happen, we'll have
1477   // to move the callback used to be a CancelableOnceCallback. By the way, for
1478   // this to happen the action (to cancel B) has to be synchronous to the
1479   // notification for request A.
1480   WorkItemList pending_items = std::move(pending_op->pending_queue);
1481   DeletePendingOp(pending_op);
1482 
1483   item->NotifyTransaction(result, entry);
1484 
1485   while (!pending_items.empty()) {
1486     item = std::move(pending_items.front());
1487     pending_items.pop_front();
1488 
1489     if (item->operation() == WI_DOOM_ENTRY) {
1490       // A queued doom request is always a race.
1491       try_restart_requests = true;
1492     } else if (result == OK) {
1493       entry = FindActiveEntry(key);
1494       if (!entry) {
1495         try_restart_requests = true;
1496       }
1497     }
1498 
1499     if (try_restart_requests) {
1500       item->NotifyTransaction(ERR_CACHE_RACE, nullptr);
1501       continue;
1502     }
1503     // At this point item->operation() is anything except Doom.
1504     if (item->operation() == WI_CREATE_ENTRY) {
1505       if (result == OK) {
1506         // Successful OpenOrCreate, Open, or Create followed by a Create.
1507         item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, nullptr);
1508       } else {
1509         if (op != WI_CREATE_ENTRY && op != WI_OPEN_OR_CREATE_ENTRY) {
1510           // Failed Open or Doom followed by a Create.
1511           item->NotifyTransaction(ERR_CACHE_RACE, nullptr);
1512           try_restart_requests = true;
1513         } else {
1514           item->NotifyTransaction(result, entry);
1515         }
1516       }
1517     }
1518     // item->operation() is OpenOrCreate or Open
1519     else if (item->operation() == WI_OPEN_OR_CREATE_ENTRY) {
1520       if ((op == WI_OPEN_ENTRY || op == WI_CREATE_ENTRY) && result != OK) {
1521         // Failed Open or Create followed by an OpenOrCreate.
1522         item->NotifyTransaction(ERR_CACHE_RACE, nullptr);
1523         try_restart_requests = true;
1524       } else {
1525         item->NotifyTransaction(result, entry);
1526       }
1527     }
1528     // item->operation() is Open.
1529     else {
1530       if (op == WI_CREATE_ENTRY && result != OK) {
1531         // Failed Create followed by an Open.
1532         item->NotifyTransaction(ERR_CACHE_RACE, nullptr);
1533         try_restart_requests = true;
1534       } else {
1535         item->NotifyTransaction(result, entry);
1536       }
1537     }
1538   }
1539 }
1540 
1541 // static
OnPendingOpComplete(base::WeakPtr<HttpCache> cache,PendingOp * pending_op,int rv)1542 void HttpCache::OnPendingOpComplete(base::WeakPtr<HttpCache> cache,
1543                                     PendingOp* pending_op,
1544                                     int rv) {
1545   if (cache.get()) {
1546     pending_op->callback_will_delete = false;
1547     cache->OnIOComplete(rv, pending_op);
1548   } else {
1549     // The callback was cancelled so we should delete the pending_op that
1550     // was used with this callback.
1551     delete pending_op;
1552   }
1553 }
1554 
1555 // static
OnPendingCreationOpComplete(base::WeakPtr<HttpCache> cache,PendingOp * pending_op,disk_cache::EntryResult result)1556 void HttpCache::OnPendingCreationOpComplete(base::WeakPtr<HttpCache> cache,
1557                                             PendingOp* pending_op,
1558                                             disk_cache::EntryResult result) {
1559   if (!cache.get()) {
1560     // The callback was cancelled so we should delete the pending_op that
1561     // was used with this callback. If |result| contains a fresh entry
1562     // it will close it automatically, since we don't release it here.
1563     delete pending_op;
1564     return;
1565   }
1566 
1567   int rv = result.net_error();
1568   pending_op->entry_opened = result.opened();
1569   pending_op->entry = result.ReleaseEntry();
1570   pending_op->callback_will_delete = false;
1571   cache->OnIOComplete(rv, pending_op);
1572 }
1573 
1574 // static
OnPendingBackendCreationOpComplete(base::WeakPtr<HttpCache> cache,PendingOp * pending_op,disk_cache::BackendResult result)1575 void HttpCache::OnPendingBackendCreationOpComplete(
1576     base::WeakPtr<HttpCache> cache,
1577     PendingOp* pending_op,
1578     disk_cache::BackendResult result) {
1579   if (!cache.get()) {
1580     // The callback was cancelled so we should delete the pending_op that
1581     // was used with this callback. If `result` contains a cache backend,
1582     // it will be destroyed with it.
1583     delete pending_op;
1584     return;
1585   }
1586 
1587   int rv = result.net_error;
1588   pending_op->backend = std::move(result.backend);
1589   pending_op->callback_will_delete = false;
1590   cache->OnIOComplete(rv, pending_op);
1591 }
1592 
OnBackendCreated(int result,PendingOp * pending_op)1593 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1594   std::unique_ptr<WorkItem> item = std::move(pending_op->writer);
1595   WorkItemOperation op = item->operation();
1596   DCHECK_EQ(WI_CREATE_BACKEND, op);
1597 
1598   if (backend_factory_.get()) {
1599     // We may end up calling OnBackendCreated multiple times if we have pending
1600     // work items. The first call saves the backend and releases the factory,
1601     // and the last call clears building_backend_.
1602     backend_factory_.reset();  // Reclaim memory.
1603     if (result == OK) {
1604       disk_cache_ = std::move(pending_op->backend);
1605       UMA_HISTOGRAM_MEMORY_KB("HttpCache.MaxFileSizeOnInit",
1606                               disk_cache_->MaxFileSize() / 1024);
1607     }
1608   }
1609 
1610   if (!pending_op->pending_queue.empty()) {
1611     std::unique_ptr<WorkItem> pending_item =
1612         std::move(pending_op->pending_queue.front());
1613     pending_op->pending_queue.pop_front();
1614     DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1615 
1616     // We want to process a single callback at a time, because the cache may
1617     // go away from the callback.
1618     pending_op->writer = std::move(pending_item);
1619 
1620     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1621         FROM_HERE, base::BindOnce(&HttpCache::OnBackendCreated, GetWeakPtr(),
1622                                   result, pending_op));
1623   } else {
1624     building_backend_ = false;
1625     DeletePendingOp(pending_op);
1626   }
1627 
1628   // The cache may be gone when we return from the callback.
1629   if (!item->DoCallback(result)) {
1630     item->NotifyTransaction(result, nullptr);
1631   }
1632 }
1633 
1634 }  // namespace net
1635