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