• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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_writers.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "base/auto_reset.h"
11 #include "base/debug/crash_logging.h"
12 #include "base/debug/dump_without_crashing.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback_helpers.h"
15 #include "base/logging.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/task/single_thread_task_runner.h"
18 #include "crypto/secure_hash.h"
19 #include "crypto/sha2.h"
20 #include "net/base/net_errors.h"
21 #include "net/disk_cache/disk_cache.h"
22 #include "net/http/http_cache_transaction.h"
23 #include "net/http/http_response_info.h"
24 #include "net/http/http_status_code.h"
25 #include "net/http/partial_data.h"
26 
27 namespace net {
28 
29 namespace {
30 
GetCacheKeyCrashKey()31 base::debug::CrashKeyString* GetCacheKeyCrashKey() {
32   static auto* crash_key = base::debug::AllocateCrashKeyString(
33       "http_cache_key", base::debug::CrashKeySize::Size256);
34   return crash_key;
35 }
36 
GetTransactionFlagsCrashKey()37 base::debug::CrashKeyString* GetTransactionFlagsCrashKey() {
38   static auto* crash_key = base::debug::AllocateCrashKeyString(
39       "http_cache_transaction", base::debug::CrashKeySize::Size256);
40   return crash_key;
41 }
42 
IsValidResponseForWriter(bool is_partial,const HttpResponseInfo * response_info)43 bool IsValidResponseForWriter(bool is_partial,
44                               const HttpResponseInfo* response_info) {
45   if (!response_info->headers.get())
46     return false;
47 
48   // Return false if the response code sent by the server is garbled.
49   // Both 200 and 304 are valid since concurrent writing is supported.
50   if (!is_partial &&
51       (response_info->headers->response_code() != net::HTTP_OK &&
52        response_info->headers->response_code() != net::HTTP_NOT_MODIFIED)) {
53     return false;
54   }
55 
56   return true;
57 }
58 
59 }  // namespace
60 
TransactionInfo(PartialData * partial_data,const bool is_truncated,HttpResponseInfo info)61 HttpCache::Writers::TransactionInfo::TransactionInfo(PartialData* partial_data,
62                                                      const bool is_truncated,
63                                                      HttpResponseInfo info)
64     : partial(partial_data), truncated(is_truncated), response_info(info) {}
65 
66 HttpCache::Writers::TransactionInfo::~TransactionInfo() = default;
67 
68 HttpCache::Writers::TransactionInfo::TransactionInfo(const TransactionInfo&) =
69     default;
70 
Writers(HttpCache * cache,HttpCache::ActiveEntry * entry)71 HttpCache::Writers::Writers(HttpCache* cache, HttpCache::ActiveEntry* entry)
72     : cache_(cache), entry_(entry) {
73   DCHECK(cache_);
74   DCHECK(entry_);
75 }
76 
77 HttpCache::Writers::~Writers() = default;
78 
Read(scoped_refptr<IOBuffer> buf,int buf_len,CompletionOnceCallback callback,Transaction * transaction)79 int HttpCache::Writers::Read(scoped_refptr<IOBuffer> buf,
80                              int buf_len,
81                              CompletionOnceCallback callback,
82                              Transaction* transaction) {
83   DCHECK(buf);
84   // TODO(https://crbug.com/1335423): Change to DCHECK_GT() or remove after bug
85   // is fixed.
86   CHECK_GT(buf_len, 0);
87   DCHECK(!callback.is_null());
88   DCHECK(transaction);
89 
90   // If another transaction invoked a Read which is currently ongoing, then
91   // this transaction waits for the read to complete and gets its buffer filled
92   // with the data returned from that read.
93   if (next_state_ != State::NONE) {
94     WaitingForRead read_info(buf, buf_len, std::move(callback));
95     waiting_for_read_.insert(std::make_pair(transaction, std::move(read_info)));
96     return ERR_IO_PENDING;
97   }
98 
99   DCHECK_EQ(next_state_, State::NONE);
100   DCHECK(callback_.is_null());
101   DCHECK_EQ(nullptr, active_transaction_);
102   DCHECK(HasTransaction(transaction));
103   active_transaction_ = transaction;
104 
105   read_buf_ = std::move(buf);
106   io_buf_len_ = buf_len;
107   next_state_ = State::NETWORK_READ;
108 
109   int rv = DoLoop(OK);
110   if (rv == ERR_IO_PENDING)
111     callback_ = std::move(callback);
112 
113   return rv;
114 }
115 
StopCaching(bool keep_entry)116 bool HttpCache::Writers::StopCaching(bool keep_entry) {
117   // If this is the only transaction in Writers, then stopping will be
118   // successful. If not, then we will not stop caching since there are
119   // other consumers waiting to read from the cache.
120   if (all_writers_.size() != 1)
121     return false;
122 
123   network_read_only_ = true;
124   if (!keep_entry) {
125     should_keep_entry_ = false;
126     cache_->WritersDoomEntryRestartTransactions(entry_);
127   }
128 
129   return true;
130 }
131 
AddTransaction(Transaction * transaction,ParallelWritingPattern initial_writing_pattern,RequestPriority priority,const TransactionInfo & info)132 void HttpCache::Writers::AddTransaction(
133     Transaction* transaction,
134     ParallelWritingPattern initial_writing_pattern,
135     RequestPriority priority,
136     const TransactionInfo& info) {
137   DCHECK(transaction);
138   ParallelWritingPattern writers_pattern;
139   DCHECK(CanAddWriters(&writers_pattern));
140 
141   DCHECK_EQ(0u, all_writers_.count(transaction));
142 
143   // Set truncation related information.
144   response_info_truncation_ = info.response_info;
145   should_keep_entry_ =
146       IsValidResponseForWriter(info.partial != nullptr, &(info.response_info));
147 
148   if (all_writers_.empty()) {
149     DCHECK_EQ(PARALLEL_WRITING_NONE, parallel_writing_pattern_);
150     parallel_writing_pattern_ = initial_writing_pattern;
151     if (parallel_writing_pattern_ != PARALLEL_WRITING_JOIN)
152       is_exclusive_ = true;
153   } else {
154     DCHECK_EQ(PARALLEL_WRITING_JOIN, parallel_writing_pattern_);
155   }
156 
157   if (info.partial && !info.truncated) {
158     DCHECK(!partial_do_not_truncate_);
159     partial_do_not_truncate_ = true;
160   }
161 
162   std::pair<Transaction*, TransactionInfo> writer(transaction, info);
163   all_writers_.insert(writer);
164 
165   priority_ = std::max(priority, priority_);
166   if (network_transaction_) {
167     network_transaction_->SetPriority(priority_);
168   }
169 }
170 
SetNetworkTransaction(Transaction * transaction,std::unique_ptr<HttpTransaction> network_transaction,std::unique_ptr<crypto::SecureHash> checksum)171 void HttpCache::Writers::SetNetworkTransaction(
172     Transaction* transaction,
173     std::unique_ptr<HttpTransaction> network_transaction,
174     std::unique_ptr<crypto::SecureHash> checksum) {
175   DCHECK_EQ(1u, all_writers_.count(transaction));
176   DCHECK(network_transaction);
177   DCHECK(!network_transaction_);
178   network_transaction_ = std::move(network_transaction);
179   network_transaction_->SetPriority(priority_);
180   DCHECK(!checksum_);
181   checksum_ = std::move(checksum);
182 }
183 
ResetNetworkTransaction()184 void HttpCache::Writers::ResetNetworkTransaction() {
185   DCHECK(is_exclusive_);
186   DCHECK_EQ(1u, all_writers_.size());
187   DCHECK(all_writers_.begin()->second.partial);
188   network_transaction_.reset();
189 }
190 
RemoveTransaction(Transaction * transaction,bool success)191 void HttpCache::Writers::RemoveTransaction(Transaction* transaction,
192                                            bool success) {
193   EraseTransaction(transaction, OK);
194 
195   if (!all_writers_.empty())
196     return;
197 
198   if (!success && ShouldTruncate())
199     TruncateEntry();
200 
201   cache_->WritersDoneWritingToEntry(entry_, success, should_keep_entry_,
202                                     TransactionSet());
203 }
204 
EraseTransaction(Transaction * transaction,int result)205 void HttpCache::Writers::EraseTransaction(Transaction* transaction,
206                                           int result) {
207   // The transaction should be part of all_writers.
208   auto it = all_writers_.find(transaction);
209   DCHECK(it != all_writers_.end());
210   EraseTransaction(it, result);
211 }
212 
213 HttpCache::Writers::TransactionMap::iterator
EraseTransaction(TransactionMap::iterator it,int result)214 HttpCache::Writers::EraseTransaction(TransactionMap::iterator it, int result) {
215   Transaction* transaction = it->first;
216   transaction->WriterAboutToBeRemovedFromEntry(result);
217 
218   auto return_it = all_writers_.erase(it);
219 
220   if (all_writers_.empty() && next_state_ == State::NONE) {
221     // This needs to be called to handle the edge case where even before Read is
222     // invoked all transactions are removed. In that case the
223     // network_transaction_ will still have a valid request info and so it
224     // should be destroyed before its consumer is destroyed (request info
225     // is a raw pointer owned by its consumer).
226     network_transaction_.reset();
227   } else {
228     UpdatePriority();
229   }
230 
231   if (active_transaction_ == transaction) {
232     active_transaction_ = nullptr;
233   } else {
234     // If waiting for read, remove it from the map.
235     waiting_for_read_.erase(transaction);
236   }
237   return return_it;
238 }
239 
UpdatePriority()240 void HttpCache::Writers::UpdatePriority() {
241   // Get the current highest priority.
242   RequestPriority current_highest = MINIMUM_PRIORITY;
243   for (auto& writer : all_writers_) {
244     Transaction* transaction = writer.first;
245     current_highest = std::max(transaction->priority(), current_highest);
246   }
247 
248   if (priority_ != current_highest) {
249     if (network_transaction_)
250       network_transaction_->SetPriority(current_highest);
251     priority_ = current_highest;
252   }
253 }
254 
CloseConnectionOnDestruction()255 void HttpCache::Writers::CloseConnectionOnDestruction() {
256   if (network_transaction_)
257     network_transaction_->CloseConnectionOnDestruction();
258 }
259 
ContainsOnlyIdleWriters() const260 bool HttpCache::Writers::ContainsOnlyIdleWriters() const {
261   return waiting_for_read_.empty() && !active_transaction_;
262 }
263 
CanAddWriters(ParallelWritingPattern * reason)264 bool HttpCache::Writers::CanAddWriters(ParallelWritingPattern* reason) {
265   *reason = parallel_writing_pattern_;
266 
267   if (all_writers_.empty())
268     return true;
269 
270   return !is_exclusive_ && !network_read_only_;
271 }
272 
ProcessFailure(int error)273 void HttpCache::Writers::ProcessFailure(int error) {
274   // Notify waiting_for_read_ of the failure. Tasks will be posted for all the
275   // transactions.
276   CompleteWaitingForReadTransactions(error);
277 
278   // Idle readers should fail when Read is invoked on them.
279   RemoveIdleWriters(error);
280 }
281 
TruncateEntry()282 void HttpCache::Writers::TruncateEntry() {
283   DCHECK(ShouldTruncate());
284   auto data = base::MakeRefCounted<PickledIOBuffer>();
285   response_info_truncation_.Persist(data->pickle(),
286                                     true /* skip_transient_headers*/,
287                                     true /* response_truncated */);
288   data->Done();
289   io_buf_len_ = data->pickle()->size();
290   entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), io_buf_len_,
291                                 base::DoNothing(), true);
292 }
293 
ShouldTruncate()294 bool HttpCache::Writers::ShouldTruncate() {
295   // Don't set the flag for sparse entries or for entries that cannot be
296   // resumed.
297   if (!should_keep_entry_ || partial_do_not_truncate_)
298     return false;
299 
300   // Check the response headers for strong validators.
301   // Note that if this is a 206, content-length was already fixed after calling
302   // PartialData::ResponseHeadersOK().
303   if (response_info_truncation_.headers->GetContentLength() <= 0 ||
304       response_info_truncation_.headers->HasHeaderValue("Accept-Ranges",
305                                                         "none") ||
306       !response_info_truncation_.headers->HasStrongValidators()) {
307     should_keep_entry_ = false;
308     return false;
309   }
310 
311   // Double check that there is something worth keeping.
312   int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
313   if (!current_size) {
314     should_keep_entry_ = false;
315     return false;
316   }
317 
318   if (response_info_truncation_.headers->HasHeader("Content-Encoding")) {
319     should_keep_entry_ = false;
320     return false;
321   }
322 
323   int64_t content_length =
324       response_info_truncation_.headers->GetContentLength();
325   if (content_length >= 0 && content_length <= current_size)
326     return false;
327 
328   return true;
329 }
330 
GetLoadState() const331 LoadState HttpCache::Writers::GetLoadState() const {
332   if (network_transaction_)
333     return network_transaction_->GetLoadState();
334   return LOAD_STATE_IDLE;
335 }
336 
WaitingForRead(scoped_refptr<IOBuffer> buf,int len,CompletionOnceCallback consumer_callback)337 HttpCache::Writers::WaitingForRead::WaitingForRead(
338     scoped_refptr<IOBuffer> buf,
339     int len,
340     CompletionOnceCallback consumer_callback)
341     : read_buf(std::move(buf)),
342       read_buf_len(len),
343       callback(std::move(consumer_callback)) {
344   DCHECK(read_buf);
345   DCHECK_GT(len, 0);
346   DCHECK(!callback.is_null());
347 }
348 
349 HttpCache::Writers::WaitingForRead::~WaitingForRead() = default;
350 HttpCache::Writers::WaitingForRead::WaitingForRead(WaitingForRead&&) = default;
351 
DoLoop(int result)352 int HttpCache::Writers::DoLoop(int result) {
353   DCHECK_NE(State::UNSET, next_state_);
354   DCHECK_NE(State::NONE, next_state_);
355 
356   int rv = result;
357   do {
358     State state = next_state_;
359     next_state_ = State::UNSET;
360     switch (state) {
361       case State::NETWORK_READ:
362         DCHECK_EQ(OK, rv);
363         rv = DoNetworkRead();
364         break;
365       case State::NETWORK_READ_COMPLETE:
366         rv = DoNetworkReadComplete(rv);
367         break;
368       case State::CACHE_WRITE_DATA:
369         rv = DoCacheWriteData(rv);
370         break;
371       case State::CACHE_WRITE_DATA_COMPLETE:
372         rv = DoCacheWriteDataComplete(rv);
373         break;
374       case State::MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE:
375         // `rv` is bytes here.
376         DCHECK_EQ(0, rv);
377         rv = DoMarkSingleKeyedCacheEntryUnusable();
378         break;
379       case State::MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE_COMPLETE:
380         rv = DoMarkSingleKeyedCacheEntryUnusableComplete(rv);
381         break;
382       case State::UNSET:
383         NOTREACHED() << "bad state";
384         rv = ERR_FAILED;
385         break;
386       case State::NONE:
387         // Do Nothing.
388         break;
389     }
390   } while (next_state_ != State::NONE && rv != ERR_IO_PENDING);
391 
392   if (next_state_ != State::NONE) {
393     if (rv != ERR_IO_PENDING && !callback_.is_null()) {
394       std::move(callback_).Run(rv);
395     }
396     return rv;
397   }
398 
399   // Save the callback as |this| may be destroyed when |cache_callback_| is run.
400   // Note that |callback_| is intentionally reset even if it is not run.
401   CompletionOnceCallback callback = std::move(callback_);
402   read_buf_ = nullptr;
403   DCHECK(!all_writers_.empty() || cache_callback_);
404   if (cache_callback_)
405     std::move(cache_callback_).Run();
406   // |this| may have been destroyed in the |cache_callback_|.
407   if (rv != ERR_IO_PENDING && !callback.is_null())
408     std::move(callback).Run(rv);
409   return rv;
410 }
411 
DoNetworkRead()412 int HttpCache::Writers::DoNetworkRead() {
413   DCHECK(network_transaction_);
414   next_state_ = State::NETWORK_READ_COMPLETE;
415 
416   // TODO(https://crbug.com/778641): This is a partial mitigation and an attempt
417   // to gather more info)
418   if (!network_transaction_) {
419     static bool reported = false;
420     if (!reported) {
421       reported = true;
422       base::debug::ScopedCrashKeyString key_info(
423           GetCacheKeyCrashKey(), active_transaction_
424                                      ? active_transaction_->key()
425                                      : "(no transaction)");
426       base::debug::ScopedCrashKeyString flags_info(
427           GetTransactionFlagsCrashKey(),
428           active_transaction_
429               ? base::StringPrintf(
430                     "mth=%s/m=%d/p=%d/t=%d/ex=%d/tc=%d/par=%d/pri=%d/nw=%zu",
431                     active_transaction_->method().c_str(),
432                     static_cast<int>(active_transaction_->mode()),
433                     static_cast<int>(active_transaction_->partial() != nullptr),
434                     static_cast<int>(active_transaction_->is_truncated()),
435                     static_cast<int>(IsExclusive()), GetTransactionsCount(),
436                     static_cast<int>(parallel_writing_pattern_),
437                     static_cast<int>(priority_), all_writers_.size())
438               : "(no transaction)");
439       base::debug::DumpWithoutCrashing();
440     }
441     return ERR_FAILED;
442   }
443 
444   CompletionOnceCallback io_callback = base::BindOnce(
445       &HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
446   return network_transaction_->Read(read_buf_.get(), io_buf_len_,
447                                     std::move(io_callback));
448 }
449 
DoNetworkReadComplete(int result)450 int HttpCache::Writers::DoNetworkReadComplete(int result) {
451   if (result < 0) {
452     next_state_ = State::NONE;
453     OnNetworkReadFailure(result);
454     return result;
455   }
456 
457   next_state_ = State::CACHE_WRITE_DATA;
458   return result;
459 }
460 
OnNetworkReadFailure(int result)461 void HttpCache::Writers::OnNetworkReadFailure(int result) {
462   ProcessFailure(result);
463 
464   if (active_transaction_) {
465     EraseTransaction(active_transaction_, result);
466   }
467   active_transaction_ = nullptr;
468 
469   if (ShouldTruncate())
470     TruncateEntry();
471 
472   SetCacheCallback(false, TransactionSet());
473 }
474 
DoCacheWriteData(int num_bytes)475 int HttpCache::Writers::DoCacheWriteData(int num_bytes) {
476   next_state_ = State::CACHE_WRITE_DATA_COMPLETE;
477   write_len_ = num_bytes;
478   if (!num_bytes || network_read_only_)
479     return num_bytes;
480 
481   int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
482   CompletionOnceCallback io_callback = base::BindOnce(
483       &HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
484 
485   int rv = 0;
486 
487   PartialData* partial = nullptr;
488   // The active transaction must be alive if this is a partial request, as
489   // partial requests are exclusive and hence will always be the active
490   // transaction.
491   // TODO(shivanisha): When partial requests support parallel writing, this
492   // assumption will not be true.
493   if (active_transaction_)
494     partial = all_writers_.find(active_transaction_)->second.partial;
495 
496   if (!partial) {
497     last_disk_cache_access_start_time_ = base::TimeTicks::Now();
498     rv = entry_->disk_entry->WriteData(kResponseContentIndex, current_size,
499                                        read_buf_.get(), num_bytes,
500                                        std::move(io_callback), true);
501   } else {
502     rv = partial->CacheWrite(entry_->GetEntry(), read_buf_.get(), num_bytes,
503                              std::move(io_callback));
504   }
505   return rv;
506 }
507 
DoCacheWriteDataComplete(int result)508 int HttpCache::Writers::DoCacheWriteDataComplete(int result) {
509   DCHECK(!all_writers_.empty());
510   DCHECK_GE(write_len_, 0);
511 
512   if (result != write_len_) {
513     next_state_ = State::NONE;
514 
515     // Note that it is possible for cache write to fail if the size of the file
516     // exceeds the per-file limit.
517     OnCacheWriteFailure();
518 
519     // |active_transaction_| can continue reading from the network.
520     return write_len_;
521   }
522 
523   if (checksum_) {
524     if (write_len_ > 0) {
525       checksum_->Update(read_buf_->data(), write_len_);
526     } else {
527       DCHECK(active_transaction_);
528       if (!active_transaction_->ResponseChecksumMatches(std::move(checksum_))) {
529         next_state_ = State::MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE;
530         return result;
531       }
532     }
533   }
534 
535   if (!last_disk_cache_access_start_time_.is_null() && active_transaction_ &&
536       !all_writers_.find(active_transaction_)->second.partial) {
537     active_transaction_->AddDiskCacheWriteTime(
538         base::TimeTicks::Now() - last_disk_cache_access_start_time_);
539     last_disk_cache_access_start_time_ = base::TimeTicks();
540   }
541 
542   next_state_ = State::NONE;
543   OnDataReceived(write_len_);
544 
545   // If we came from DoMarkSingleKeyedCacheUnusableComplete() then  result  will
546   // be the size of the metadata that was written. But DoLoop() needs to know
547   // the number of bytes of data that were written. So return that instead.
548   return write_len_;
549 }
550 
DoMarkSingleKeyedCacheEntryUnusable()551 int HttpCache::Writers::DoMarkSingleKeyedCacheEntryUnusable() {
552   // `response_info_truncation_` is not actually truncated.
553   // TODO(ricea): Maybe change the name of the member?
554   response_info_truncation_.single_keyed_cache_entry_unusable = true;
555   next_state_ = State::MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE_COMPLETE;
556 
557   // Update cache metadata. This is a subset of what
558   // HttpCache::Transaction::WriteResponseInfoToEntry does.
559   auto data = base::MakeRefCounted<PickledIOBuffer>();
560   response_info_truncation_.Persist(data->pickle(),
561                                     /*skip_transient_headers=*/true,
562                                     /*response_truncated=*/false);
563   data->Done();
564   io_buf_len_ = data->pickle()->size();
565   CompletionOnceCallback io_callback = base::BindOnce(
566       &HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
567   return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(),
568                                        io_buf_len_, std::move(io_callback),
569                                        true);
570 }
571 
DoMarkSingleKeyedCacheEntryUnusableComplete(int result)572 int HttpCache::Writers::DoMarkSingleKeyedCacheEntryUnusableComplete(
573     int result) {
574   DCHECK(!checksum_);
575   // We run DoCacheWriteDataComplete again, but this time `checksum_` is null so
576   // we won't come back here.
577   next_state_ = State::CACHE_WRITE_DATA_COMPLETE;
578   return result < 0 ? result : write_len_;
579 }
580 
OnDataReceived(int result)581 void HttpCache::Writers::OnDataReceived(int result) {
582   DCHECK(!all_writers_.empty());
583 
584   auto it = all_writers_.find(active_transaction_);
585   bool is_partial =
586       active_transaction_ != nullptr && it->second.partial != nullptr;
587 
588   // Partial transaction will process the result, return from here.
589   // This is done because partial requests handling require an awareness of both
590   // headers and body state machines as they might have to go to the headers
591   // phase for the next range, so it cannot be completely handled here.
592   if (is_partial) {
593     active_transaction_ = nullptr;
594     return;
595   }
596 
597   if (result == 0) {
598     // Check if the response is actually completed or if not, attempt to mark
599     // the entry as truncated in OnNetworkReadFailure.
600     int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
601     DCHECK(network_transaction_);
602     const HttpResponseInfo* response_info =
603         network_transaction_->GetResponseInfo();
604     int64_t content_length = response_info->headers->GetContentLength();
605     if (content_length >= 0 && content_length > current_size) {
606       OnNetworkReadFailure(result);
607       return;
608     }
609 
610     if (active_transaction_) {
611       EraseTransaction(active_transaction_, result);
612     }
613     active_transaction_ = nullptr;
614     CompleteWaitingForReadTransactions(write_len_);
615 
616     // Invoke entry processing.
617     DCHECK(ContainsOnlyIdleWriters());
618     TransactionSet make_readers;
619     for (auto& writer : all_writers_) {
620       make_readers.insert(writer.first);
621     }
622     all_writers_.clear();
623     SetCacheCallback(true, make_readers);
624     // We assume the set callback will be called immediately.
625     DCHECK_EQ(next_state_, State::NONE);
626     return;
627   }
628 
629   // Notify waiting_for_read_. Tasks will be posted for all the
630   // transactions.
631   CompleteWaitingForReadTransactions(write_len_);
632 
633   active_transaction_ = nullptr;
634 }
635 
OnCacheWriteFailure()636 void HttpCache::Writers::OnCacheWriteFailure() {
637   DLOG(ERROR) << "failed to write response data to cache";
638 
639   ProcessFailure(ERR_CACHE_WRITE_FAILURE);
640 
641   // Now writers will only be reading from the network.
642   network_read_only_ = true;
643 
644   active_transaction_ = nullptr;
645 
646   should_keep_entry_ = false;
647   if (all_writers_.empty()) {
648     SetCacheCallback(false, TransactionSet());
649   } else {
650     cache_->WritersDoomEntryRestartTransactions(entry_);
651   }
652 }
653 
CompleteWaitingForReadTransactions(int result)654 void HttpCache::Writers::CompleteWaitingForReadTransactions(int result) {
655   for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end();) {
656     Transaction* transaction = it->first;
657     int callback_result = result;
658 
659     if (result >= 0) {  // success
660       // Save the data in the waiting transaction's read buffer.
661       it->second.write_len = std::min(it->second.read_buf_len, result);
662       memcpy(it->second.read_buf->data(), read_buf_->data(),
663              it->second.write_len);
664       callback_result = it->second.write_len;
665     }
666 
667     // Post task to notify transaction.
668     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
669         FROM_HERE,
670         base::BindOnce(std::move(it->second.callback), callback_result));
671 
672     it = waiting_for_read_.erase(it);
673 
674     // If its response completion or failure, this transaction needs to be
675     // removed from writers.
676     if (result <= 0) {
677       EraseTransaction(transaction, result);
678     }
679   }
680 }
681 
RemoveIdleWriters(int result)682 void HttpCache::Writers::RemoveIdleWriters(int result) {
683   // Since this is only for idle transactions, waiting_for_read_
684   // should be empty.
685   DCHECK(waiting_for_read_.empty());
686   for (auto it = all_writers_.begin(); it != all_writers_.end();) {
687     Transaction* transaction = it->first;
688     if (transaction == active_transaction_) {
689       it++;
690       continue;
691     }
692     it = EraseTransaction(it, result);
693   }
694 }
695 
SetCacheCallback(bool success,const TransactionSet & make_readers)696 void HttpCache::Writers::SetCacheCallback(bool success,
697                                           const TransactionSet& make_readers) {
698   DCHECK(!cache_callback_);
699   cache_callback_ = base::BindOnce(&HttpCache::WritersDoneWritingToEntry,
700                                    cache_->GetWeakPtr(), entry_, success,
701                                    should_keep_entry_, make_readers);
702 }
703 
OnIOComplete(int result)704 void HttpCache::Writers::OnIOComplete(int result) {
705   DoLoop(result);
706 }
707 
708 }  // namespace net
709