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