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_transaction.h"
6
7 #include "base/time/time.h"
8 #include "build/build_config.h" // For IS_POSIX
9
10 #if BUILDFLAG(IS_POSIX)
11 #include <unistd.h>
12 #endif
13
14 #include <algorithm>
15 #include <memory>
16 #include <string>
17 #include <type_traits>
18 #include <utility>
19
20 #include "base/auto_reset.h"
21 #include "base/compiler_specific.h"
22 #include "base/containers/fixed_flat_set.h"
23 #include "base/feature_list.h"
24 #include "base/format_macros.h"
25 #include "base/functional/bind.h"
26 #include "base/functional/callback_helpers.h"
27 #include "base/location.h"
28 #include "base/memory/raw_ptr_exclusion.h"
29 #include "base/metrics/histogram_functions.h"
30 #include "base/metrics/histogram_macros.h"
31 #include "base/power_monitor/power_monitor.h"
32 #include "base/strings/string_piece.h"
33 #include "base/strings/string_util.h" // For EqualsCaseInsensitiveASCII.
34 #include "base/task/single_thread_task_runner.h"
35 #include "base/time/clock.h"
36 #include "base/trace_event/common/trace_event_common.h"
37 #include "base/values.h"
38 #include "net/base/auth.h"
39 #include "net/base/features.h"
40 #include "net/base/load_flags.h"
41 #include "net/base/load_timing_info.h"
42 #include "net/base/trace_constants.h"
43 #include "net/base/tracing.h"
44 #include "net/base/transport_info.h"
45 #include "net/base/upload_data_stream.h"
46 #include "net/cert/cert_status_flags.h"
47 #include "net/cert/x509_certificate.h"
48 #include "net/disk_cache/disk_cache.h"
49 #include "net/http/http_cache_writers.h"
50 #include "net/http/http_log_util.h"
51 #include "net/http/http_network_session.h"
52 #include "net/http/http_request_info.h"
53 #include "net/http/http_response_headers.h"
54 #include "net/http/http_status_code.h"
55 #include "net/http/http_util.h"
56 #include "net/log/net_log_event_type.h"
57 #include "net/ssl/ssl_cert_request_info.h"
58 #include "net/ssl/ssl_config_service.h"
59
60 using base::Time;
61 using base::TimeTicks;
62
63 namespace net {
64
65 using CacheEntryStatus = HttpResponseInfo::CacheEntryStatus;
66
67 namespace {
68
69 constexpr base::TimeDelta kStaleRevalidateTimeout = base::Seconds(60);
70
GetNextTraceId(HttpCache * cache)71 uint64_t GetNextTraceId(HttpCache* cache) {
72 static uint32_t sNextTraceId = 0;
73
74 DCHECK(cache);
75 return (reinterpret_cast<uint64_t>(cache) << 32) | sNextTraceId++;
76 }
77
78 // From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6
79 // a "non-error response" is one with a 2xx (Successful) or 3xx
80 // (Redirection) status code.
NonErrorResponse(int status_code)81 bool NonErrorResponse(int status_code) {
82 int status_code_range = status_code / 100;
83 return status_code_range == 2 || status_code_range == 3;
84 }
85
IsOnBatteryPower()86 bool IsOnBatteryPower() {
87 if (base::PowerMonitor::IsInitialized()) {
88 return base::PowerMonitor::IsOnBatteryPower();
89 }
90 return false;
91 }
92
93 enum ExternallyConditionalizedType {
94 EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION,
95 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE,
96 EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS,
97 EXTERNALLY_CONDITIONALIZED_MAX
98 };
99
ShouldByPassCacheForFirstPartySets(const std::optional<int64_t> & clear_at_run_id,const std::optional<int64_t> & written_at_run_id)100 bool ShouldByPassCacheForFirstPartySets(
101 const std::optional<int64_t>& clear_at_run_id,
102 const std::optional<int64_t>& written_at_run_id) {
103 return clear_at_run_id.has_value() &&
104 (!written_at_run_id.has_value() ||
105 written_at_run_id.value() < clear_at_run_id.value());
106 }
107
108 struct HeaderNameAndValue {
109 const char* name;
110 const char* value;
111 };
112
113 // If the request includes one of these request headers, then avoid caching
114 // to avoid getting confused.
115 constexpr HeaderNameAndValue kPassThroughHeaders[] = {
116 {"if-unmodified-since", nullptr}, // causes unexpected 412s
117 {"if-match", nullptr}, // causes unexpected 412s
118 {"if-range", nullptr},
119 {nullptr, nullptr}};
120
121 struct ValidationHeaderInfo {
122 const char* request_header_name;
123 const char* related_response_header_name;
124 };
125
126 constexpr ValidationHeaderInfo kValidationHeaders[] = {
127 {"if-modified-since", "last-modified"},
128 {"if-none-match", "etag"},
129 };
130
131 // If the request includes one of these request headers, then avoid reusing
132 // our cached copy if any.
133 constexpr HeaderNameAndValue kForceFetchHeaders[] = {
134 {"cache-control", "no-cache"},
135 {"pragma", "no-cache"},
136 {nullptr, nullptr}};
137
138 // If the request includes one of these request headers, then force our
139 // cached copy (if any) to be revalidated before reusing it.
140 constexpr HeaderNameAndValue kForceValidateHeaders[] = {
141 {"cache-control", "max-age=0"},
142 {nullptr, nullptr}};
143
HeaderMatches(const HttpRequestHeaders & headers,const HeaderNameAndValue * search)144 bool HeaderMatches(const HttpRequestHeaders& headers,
145 const HeaderNameAndValue* search) {
146 for (; search->name; ++search) {
147 std::string header_value;
148 if (!headers.GetHeader(search->name, &header_value)) {
149 continue;
150 }
151
152 if (!search->value) {
153 return true;
154 }
155
156 HttpUtil::ValuesIterator v(header_value.begin(), header_value.end(), ',');
157 while (v.GetNext()) {
158 if (base::EqualsCaseInsensitiveASCII(v.value_piece(), search->value)) {
159 return true;
160 }
161 }
162 }
163 return false;
164 }
165
166 } // namespace
167
168 #define CACHE_STATUS_HISTOGRAMS(type) \
169 UMA_HISTOGRAM_ENUMERATION("HttpCache.Pattern" type, cache_entry_status_, \
170 CacheEntryStatus::ENTRY_MAX)
171
172 #define IS_NO_STORE_HISTOGRAMS(type, is_no_store) \
173 base::UmaHistogramBoolean("HttpCache.IsNoStore" type, is_no_store)
174
175 //-----------------------------------------------------------------------------
176
Transaction(RequestPriority priority,HttpCache * cache)177 HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache)
178 : trace_id_(GetNextTraceId(cache)),
179 priority_(priority),
180 cache_(cache->GetWeakPtr()) {
181 static_assert(HttpCache::Transaction::kNumValidationHeaders ==
182 std::size(kValidationHeaders),
183 "invalid number of validation headers");
184
185 io_callback_ = base::BindRepeating(&Transaction::OnIOComplete,
186 weak_factory_.GetWeakPtr());
187 cache_io_callback_ = base::BindRepeating(&Transaction::OnCacheIOComplete,
188 weak_factory_.GetWeakPtr());
189 }
190
~Transaction()191 HttpCache::Transaction::~Transaction() {
192 TRACE_EVENT_END("net", perfetto::Track(trace_id_));
193 RecordHistograms();
194
195 // We may have to issue another IO, but we should never invoke the callback_
196 // after this point.
197 callback_.Reset();
198
199 if (cache_) {
200 if (entry_) {
201 DoneWithEntry(false /* entry_is_complete */);
202 } else if (cache_pending_) {
203 cache_->RemovePendingTransaction(this);
204 }
205 }
206 }
207
mode() const208 HttpCache::Transaction::Mode HttpCache::Transaction::mode() const {
209 return mode_;
210 }
211
GetWriterLoadState() const212 LoadState HttpCache::Transaction::GetWriterLoadState() const {
213 const HttpTransaction* transaction = network_transaction();
214 if (transaction) {
215 return transaction->GetLoadState();
216 }
217 if (entry_ || !request_) {
218 return LOAD_STATE_IDLE;
219 }
220 return LOAD_STATE_WAITING_FOR_CACHE;
221 }
222
net_log() const223 const NetLogWithSource& HttpCache::Transaction::net_log() const {
224 return net_log_;
225 }
226
Start(const HttpRequestInfo * request,CompletionOnceCallback callback,const NetLogWithSource & net_log)227 int HttpCache::Transaction::Start(const HttpRequestInfo* request,
228 CompletionOnceCallback callback,
229 const NetLogWithSource& net_log) {
230 DCHECK(request);
231 DCHECK(request->IsConsistent());
232 DCHECK(!callback.is_null());
233 TRACE_EVENT_BEGIN("net", "HttpCacheTransaction", perfetto::Track(trace_id_),
234 "url", request->url.spec());
235
236 // Ensure that we only have one asynchronous call at a time.
237 DCHECK(callback_.is_null());
238 DCHECK(!reading_);
239 DCHECK(!network_trans_.get());
240 DCHECK(!entry_);
241 DCHECK_EQ(next_state_, STATE_NONE);
242
243 if (!cache_.get()) {
244 return ERR_UNEXPECTED;
245 }
246
247 initial_request_ = request;
248 SetRequest(net_log);
249
250 // We have to wait until the backend is initialized so we start the SM.
251 next_state_ = STATE_GET_BACKEND;
252 int rv = DoLoop(OK);
253
254 // Setting this here allows us to check for the existence of a callback_ to
255 // determine if we are still inside Start.
256 if (rv == ERR_IO_PENDING) {
257 callback_ = std::move(callback);
258 }
259
260 return rv;
261 }
262
RestartIgnoringLastError(CompletionOnceCallback callback)263 int HttpCache::Transaction::RestartIgnoringLastError(
264 CompletionOnceCallback callback) {
265 DCHECK(!callback.is_null());
266
267 // Ensure that we only have one asynchronous call at a time.
268 DCHECK(callback_.is_null());
269
270 if (!cache_.get()) {
271 return ERR_UNEXPECTED;
272 }
273
274 int rv = RestartNetworkRequest();
275
276 if (rv == ERR_IO_PENDING) {
277 callback_ = std::move(callback);
278 }
279
280 return rv;
281 }
282
RestartWithCertificate(scoped_refptr<X509Certificate> client_cert,scoped_refptr<SSLPrivateKey> client_private_key,CompletionOnceCallback callback)283 int HttpCache::Transaction::RestartWithCertificate(
284 scoped_refptr<X509Certificate> client_cert,
285 scoped_refptr<SSLPrivateKey> client_private_key,
286 CompletionOnceCallback callback) {
287 DCHECK(!callback.is_null());
288
289 // Ensure that we only have one asynchronous call at a time.
290 DCHECK(callback_.is_null());
291
292 if (!cache_.get()) {
293 return ERR_UNEXPECTED;
294 }
295
296 int rv = RestartNetworkRequestWithCertificate(std::move(client_cert),
297 std::move(client_private_key));
298
299 if (rv == ERR_IO_PENDING) {
300 callback_ = std::move(callback);
301 }
302
303 return rv;
304 }
305
RestartWithAuth(const AuthCredentials & credentials,CompletionOnceCallback callback)306 int HttpCache::Transaction::RestartWithAuth(const AuthCredentials& credentials,
307 CompletionOnceCallback callback) {
308 DCHECK(auth_response_.headers.get());
309 DCHECK(!callback.is_null());
310
311 // Ensure that we only have one asynchronous call at a time.
312 DCHECK(callback_.is_null());
313
314 if (!cache_.get()) {
315 return ERR_UNEXPECTED;
316 }
317
318 // Clear the intermediate response since we are going to start over.
319 SetAuthResponse(HttpResponseInfo());
320
321 int rv = RestartNetworkRequestWithAuth(credentials);
322
323 if (rv == ERR_IO_PENDING) {
324 callback_ = std::move(callback);
325 }
326
327 return rv;
328 }
329
IsReadyToRestartForAuth()330 bool HttpCache::Transaction::IsReadyToRestartForAuth() {
331 if (!network_trans_.get()) {
332 return false;
333 }
334 return network_trans_->IsReadyToRestartForAuth();
335 }
336
Read(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)337 int HttpCache::Transaction::Read(IOBuffer* buf,
338 int buf_len,
339 CompletionOnceCallback callback) {
340 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::Read",
341 perfetto::Track(trace_id_), "buf_len", buf_len);
342
343 DCHECK_EQ(next_state_, STATE_NONE);
344 DCHECK(buf);
345 DCHECK_GT(buf_len, 0);
346 DCHECK(!callback.is_null());
347
348 DCHECK(callback_.is_null());
349
350 if (!cache_.get()) {
351 return ERR_UNEXPECTED;
352 }
353
354 // If we have an intermediate auth response at this point, then it means the
355 // user wishes to read the network response (the error page). If there is a
356 // previous response in the cache then we should leave it intact.
357 if (auth_response_.headers.get() && mode_ != NONE) {
358 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
359 DCHECK(mode_ & WRITE);
360 bool stopped = StopCachingImpl(mode_ == READ_WRITE);
361 DCHECK(stopped);
362 }
363
364 reading_ = true;
365 read_buf_ = buf;
366 read_buf_len_ = buf_len;
367 int rv = TransitionToReadingState();
368 if (rv != OK || next_state_ == STATE_NONE) {
369 return rv;
370 }
371
372 rv = DoLoop(OK);
373
374 if (rv == ERR_IO_PENDING) {
375 DCHECK(callback_.is_null());
376 callback_ = std::move(callback);
377 }
378 return rv;
379 }
380
TransitionToReadingState()381 int HttpCache::Transaction::TransitionToReadingState() {
382 if (!entry_) {
383 if (network_trans_) {
384 // This can happen when the request should be handled exclusively by
385 // the network layer (skipping the cache entirely using
386 // LOAD_DISABLE_CACHE) or there was an error during the headers phase
387 // due to which the transaction cannot write to the cache or the consumer
388 // is reading the auth response from the network.
389 // TODO(http://crbug.com/740947) to get rid of this state in future.
390 next_state_ = STATE_NETWORK_READ;
391
392 return OK;
393 }
394
395 // If there is no network, and no cache entry, then there is nothing to read
396 // from.
397 next_state_ = STATE_NONE;
398
399 // An error state should be set for the next read, else this transaction
400 // should have been terminated once it reached this state. To assert we
401 // could dcheck that shared_writing_error_ is set to a valid error value but
402 // in some specific conditions (http://crbug.com/806344) it's possible that
403 // the consumer does an extra Read in which case the assert will fail.
404 return shared_writing_error_;
405 }
406
407 // If entry_ is present, the transaction is either a member of entry_->writers
408 // or readers.
409 if (!InWriters()) {
410 // Since transaction is not a writer and we are in Read(), it must be a
411 // reader.
412 DCHECK(entry_->TransactionInReaders(this));
413 DCHECK(mode_ == READ || (mode_ == READ_WRITE && partial_));
414 next_state_ = STATE_CACHE_READ_DATA;
415 return OK;
416 }
417
418 DCHECK(mode_ & WRITE || mode_ == NONE);
419
420 // If it's a writer and it is partial then it may need to read from the cache
421 // or from the network based on whether network transaction is present or not.
422 if (partial_) {
423 if (entry_->writers()->network_transaction()) {
424 next_state_ = STATE_NETWORK_READ_CACHE_WRITE;
425 } else {
426 next_state_ = STATE_CACHE_READ_DATA;
427 }
428 return OK;
429 }
430
431 // Full request.
432 // If it's a writer and a full request then it may read from the cache if its
433 // offset is behind the current offset else from the network.
434 int disk_entry_size = entry_->GetEntry()->GetDataSize(kResponseContentIndex);
435 if (read_offset_ == disk_entry_size ||
436 entry_->writers()->network_read_only()) {
437 next_state_ = STATE_NETWORK_READ_CACHE_WRITE;
438 } else {
439 DCHECK_LT(read_offset_, disk_entry_size);
440 next_state_ = STATE_CACHE_READ_DATA;
441 }
442 return OK;
443 }
444
StopCaching()445 void HttpCache::Transaction::StopCaching() {
446 // We really don't know where we are now. Hopefully there is no operation in
447 // progress, but nothing really prevents this method to be called after we
448 // returned ERR_IO_PENDING. We cannot attempt to truncate the entry at this
449 // point because we need the state machine for that (and even if we are really
450 // free, that would be an asynchronous operation). In other words, keep the
451 // entry how it is (it will be marked as truncated at destruction), and let
452 // the next piece of code that executes know that we are now reading directly
453 // from the net.
454 if (cache_.get() && (mode_ & WRITE) && !is_sparse_ && !range_requested_ &&
455 network_transaction()) {
456 StopCachingImpl(false);
457 }
458 }
459
GetTotalReceivedBytes() const460 int64_t HttpCache::Transaction::GetTotalReceivedBytes() const {
461 int64_t total_received_bytes = network_transaction_info_.total_received_bytes;
462 const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
463 if (transaction) {
464 total_received_bytes += transaction->GetTotalReceivedBytes();
465 }
466 return total_received_bytes;
467 }
468
GetTotalSentBytes() const469 int64_t HttpCache::Transaction::GetTotalSentBytes() const {
470 int64_t total_sent_bytes = network_transaction_info_.total_sent_bytes;
471 const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
472 if (transaction) {
473 total_sent_bytes += transaction->GetTotalSentBytes();
474 }
475 return total_sent_bytes;
476 }
477
DoneReading()478 void HttpCache::Transaction::DoneReading() {
479 if (cache_.get() && entry_) {
480 DCHECK_NE(mode_, UPDATE);
481 DoneWithEntry(true);
482 }
483 }
484
GetResponseInfo() const485 const HttpResponseInfo* HttpCache::Transaction::GetResponseInfo() const {
486 // Null headers means we encountered an error or haven't a response yet
487 if (auth_response_.headers.get()) {
488 DCHECK_EQ(cache_entry_status_, auth_response_.cache_entry_status)
489 << "These must be in sync via SetResponse and SetAuthResponse.";
490 return &auth_response_;
491 }
492 // TODO(https://crbug.com/1219402): This should check in `response_`
493 return &response_;
494 }
495
GetLoadState() const496 LoadState HttpCache::Transaction::GetLoadState() const {
497 // If there's no pending callback, the ball is not in the
498 // HttpCache::Transaction's court, whatever else may be going on.
499 if (!callback_) {
500 return LOAD_STATE_IDLE;
501 }
502
503 LoadState state = GetWriterLoadState();
504 if (state != LOAD_STATE_WAITING_FOR_CACHE) {
505 return state;
506 }
507
508 if (cache_.get()) {
509 return cache_->GetLoadStateForPendingTransaction(this);
510 }
511
512 return LOAD_STATE_IDLE;
513 }
514
SetQuicServerInfo(QuicServerInfo * quic_server_info)515 void HttpCache::Transaction::SetQuicServerInfo(
516 QuicServerInfo* quic_server_info) {}
517
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const518 bool HttpCache::Transaction::GetLoadTimingInfo(
519 LoadTimingInfo* load_timing_info) const {
520 const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
521 if (transaction) {
522 return transaction->GetLoadTimingInfo(load_timing_info);
523 }
524
525 if (network_transaction_info_.old_network_trans_load_timing) {
526 *load_timing_info =
527 *network_transaction_info_.old_network_trans_load_timing;
528 return true;
529 }
530
531 if (first_cache_access_since_.is_null()) {
532 return false;
533 }
534
535 // If the cache entry was opened, return that time.
536 load_timing_info->send_start = first_cache_access_since_;
537 // This time doesn't make much sense when reading from the cache, so just use
538 // the same time as send_start.
539 load_timing_info->send_end = first_cache_access_since_;
540 // Provide the time immediately before parsing a cached entry.
541 load_timing_info->receive_headers_start = read_headers_since_;
542 return true;
543 }
544
GetRemoteEndpoint(IPEndPoint * endpoint) const545 bool HttpCache::Transaction::GetRemoteEndpoint(IPEndPoint* endpoint) const {
546 const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
547 if (transaction) {
548 return transaction->GetRemoteEndpoint(endpoint);
549 }
550
551 if (!network_transaction_info_.old_remote_endpoint.address().empty()) {
552 *endpoint = network_transaction_info_.old_remote_endpoint;
553 return true;
554 }
555
556 return false;
557 }
558
PopulateNetErrorDetails(NetErrorDetails * details) const559 void HttpCache::Transaction::PopulateNetErrorDetails(
560 NetErrorDetails* details) const {
561 const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
562 if (transaction) {
563 return transaction->PopulateNetErrorDetails(details);
564 }
565 return;
566 }
567
SetPriority(RequestPriority priority)568 void HttpCache::Transaction::SetPriority(RequestPriority priority) {
569 priority_ = priority;
570
571 if (network_trans_) {
572 network_trans_->SetPriority(priority_);
573 }
574
575 if (InWriters()) {
576 DCHECK(!network_trans_ || partial_);
577 entry_->writers()->UpdatePriority();
578 }
579 }
580
SetWebSocketHandshakeStreamCreateHelper(WebSocketHandshakeStreamBase::CreateHelper * create_helper)581 void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper(
582 WebSocketHandshakeStreamBase::CreateHelper* create_helper) {
583 websocket_handshake_stream_base_create_helper_ = create_helper;
584
585 // TODO(shivanisha). Since this function must be invoked before Start() as
586 // per the API header, a network transaction should not exist at that point.
587 HttpTransaction* transaction = network_transaction();
588 if (transaction) {
589 transaction->SetWebSocketHandshakeStreamCreateHelper(create_helper);
590 }
591 }
592
SetBeforeNetworkStartCallback(BeforeNetworkStartCallback callback)593 void HttpCache::Transaction::SetBeforeNetworkStartCallback(
594 BeforeNetworkStartCallback callback) {
595 DCHECK(!network_trans_);
596 before_network_start_callback_ = std::move(callback);
597 }
598
SetConnectedCallback(const ConnectedCallback & callback)599 void HttpCache::Transaction::SetConnectedCallback(
600 const ConnectedCallback& callback) {
601 DCHECK(!network_trans_);
602 connected_callback_ = callback;
603 }
604
SetRequestHeadersCallback(RequestHeadersCallback callback)605 void HttpCache::Transaction::SetRequestHeadersCallback(
606 RequestHeadersCallback callback) {
607 DCHECK(!network_trans_);
608 request_headers_callback_ = std::move(callback);
609 }
610
SetResponseHeadersCallback(ResponseHeadersCallback callback)611 void HttpCache::Transaction::SetResponseHeadersCallback(
612 ResponseHeadersCallback callback) {
613 DCHECK(!network_trans_);
614 response_headers_callback_ = std::move(callback);
615 }
616
SetEarlyResponseHeadersCallback(ResponseHeadersCallback callback)617 void HttpCache::Transaction::SetEarlyResponseHeadersCallback(
618 ResponseHeadersCallback callback) {
619 DCHECK(!network_trans_);
620 early_response_headers_callback_ = std::move(callback);
621 }
622
SetModifyRequestHeadersCallback(base::RepeatingCallback<void (net::HttpRequestHeaders *)> callback)623 void HttpCache::Transaction::SetModifyRequestHeadersCallback(
624 base::RepeatingCallback<void(net::HttpRequestHeaders*)> callback) {
625 // This method should not be called for this class.
626 NOTREACHED();
627 }
628
SetIsSharedDictionaryReadAllowedCallback(base::RepeatingCallback<bool ()> callback)629 void HttpCache::Transaction::SetIsSharedDictionaryReadAllowedCallback(
630 base::RepeatingCallback<bool()> callback) {
631 DCHECK(!network_trans_);
632 is_shared_dictionary_read_allowed_callback_ = std::move(callback);
633 }
634
ResumeNetworkStart()635 int HttpCache::Transaction::ResumeNetworkStart() {
636 if (network_trans_) {
637 return network_trans_->ResumeNetworkStart();
638 }
639 return ERR_UNEXPECTED;
640 }
641
GetConnectionAttempts() const642 ConnectionAttempts HttpCache::Transaction::GetConnectionAttempts() const {
643 ConnectionAttempts attempts;
644 const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
645 if (transaction) {
646 attempts = transaction->GetConnectionAttempts();
647 }
648
649 attempts.insert(attempts.begin(),
650 network_transaction_info_.old_connection_attempts.begin(),
651 network_transaction_info_.old_connection_attempts.end());
652 return attempts;
653 }
654
CloseConnectionOnDestruction()655 void HttpCache::Transaction::CloseConnectionOnDestruction() {
656 if (network_trans_) {
657 network_trans_->CloseConnectionOnDestruction();
658 } else if (InWriters()) {
659 entry_->writers()->CloseConnectionOnDestruction();
660 }
661 }
662
IsMdlMatchForMetrics() const663 bool HttpCache::Transaction::IsMdlMatchForMetrics() const {
664 if (network_transaction_info_.previous_mdl_match_for_metrics) {
665 return true;
666 }
667 const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
668 if (transaction) {
669 return transaction->IsMdlMatchForMetrics();
670 } else {
671 return false;
672 }
673 }
674
SetValidatingCannotProceed()675 void HttpCache::Transaction::SetValidatingCannotProceed() {
676 DCHECK(!reading_);
677 // Ensure this transaction is waiting for a callback.
678 DCHECK_NE(STATE_UNSET, next_state_);
679
680 next_state_ = STATE_HEADERS_PHASE_CANNOT_PROCEED;
681 entry_.reset();
682 }
683
WriterAboutToBeRemovedFromEntry(int result)684 void HttpCache::Transaction::WriterAboutToBeRemovedFromEntry(int result) {
685 TRACE_EVENT_INSTANT("net",
686 "HttpCacheTransaction::WriterAboutToBeRemovedFromEntry",
687 perfetto::Track(trace_id_));
688 // Since the transaction can no longer access the network transaction, save
689 // all network related info now.
690 if (moved_network_transaction_to_writers_ &&
691 entry_->writers()->network_transaction()) {
692 SaveNetworkTransactionInfo(*(entry_->writers()->network_transaction()));
693 }
694
695 entry_.reset();
696 mode_ = NONE;
697
698 // Transactions in the midst of a Read call through writers will get any error
699 // code through the IO callback but for idle transactions/transactions reading
700 // from the cache, the error for a future Read must be stored here.
701 if (result < 0) {
702 shared_writing_error_ = result;
703 }
704 }
705
WriteModeTransactionAboutToBecomeReader()706 void HttpCache::Transaction::WriteModeTransactionAboutToBecomeReader() {
707 TRACE_EVENT_INSTANT(
708 "net", "HttpCacheTransaction::WriteModeTransactionAboutToBecomeReader",
709 perfetto::Track(trace_id_));
710 mode_ = READ;
711 if (moved_network_transaction_to_writers_ &&
712 entry_->writers()->network_transaction()) {
713 SaveNetworkTransactionInfo(*(entry_->writers()->network_transaction()));
714 }
715 }
716
AddDiskCacheWriteTime(base::TimeDelta elapsed)717 void HttpCache::Transaction::AddDiskCacheWriteTime(base::TimeDelta elapsed) {
718 total_disk_cache_write_time_ += elapsed;
719 }
720
721 //-----------------------------------------------------------------------------
722
723 // A few common patterns: (Foo* means Foo -> FooComplete)
724 //
725 // 1. Not-cached entry:
726 // Start():
727 // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
728 // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
729 // CacheWriteResponse* -> TruncateCachedData* -> PartialHeadersReceived ->
730 // FinishHeaders*
731 //
732 // Read():
733 // NetworkReadCacheWrite*/CacheReadData* (if other writers are also writing to
734 // the cache)
735 //
736 // 2. Cached entry, no validation:
737 // Start():
738 // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
739 // CacheReadResponse* -> CacheDispatchValidation ->
740 // BeginPartialCacheValidation() -> BeginCacheValidation() ->
741 // ConnectedCallback* -> SetupEntryForRead() -> FinishHeaders*
742 //
743 // Read():
744 // CacheReadData*
745 //
746 // 3. Cached entry, validation (304):
747 // Start():
748 // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
749 // CacheReadResponse* -> CacheDispatchValidation ->
750 // BeginPartialCacheValidation() -> BeginCacheValidation() -> SendRequest* ->
751 // SuccessfulSendRequest -> UpdateCachedResponse -> CacheWriteUpdatedResponse*
752 // -> UpdateCachedResponseComplete -> OverwriteCachedResponse ->
753 // PartialHeadersReceived -> FinishHeaders*
754 //
755 // Read():
756 // CacheReadData*
757 //
758 // 4. Cached entry, validation and replace (200):
759 // Start():
760 // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
761 // CacheReadResponse* -> CacheDispatchValidation ->
762 // BeginPartialCacheValidation() -> BeginCacheValidation() -> SendRequest* ->
763 // SuccessfulSendRequest -> OverwriteCachedResponse -> CacheWriteResponse* ->
764 // DoTruncateCachedData* -> PartialHeadersReceived -> FinishHeaders*
765 //
766 // Read():
767 // NetworkReadCacheWrite*/CacheReadData* (if other writers are also writing to
768 // the cache)
769 //
770 // 5. Sparse entry, partially cached, byte range request:
771 // Start():
772 // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
773 // CacheReadResponse* -> CacheDispatchValidation ->
774 // BeginPartialCacheValidation() -> CacheQueryData* ->
775 // ValidateEntryHeadersAndContinue() -> StartPartialCacheValidation ->
776 // CompletePartialCacheValidation -> BeginCacheValidation() -> SendRequest* ->
777 // SuccessfulSendRequest -> UpdateCachedResponse -> CacheWriteUpdatedResponse*
778 // -> UpdateCachedResponseComplete -> OverwriteCachedResponse ->
779 // PartialHeadersReceived -> FinishHeaders*
780 //
781 // Read() 1:
782 // NetworkReadCacheWrite*
783 //
784 // Read() 2:
785 // NetworkReadCacheWrite* -> StartPartialCacheValidation ->
786 // CompletePartialCacheValidation -> ConnectedCallback* -> CacheReadData*
787 //
788 // Read() 3:
789 // CacheReadData* -> StartPartialCacheValidation ->
790 // CompletePartialCacheValidation -> BeginCacheValidation() -> SendRequest* ->
791 // SuccessfulSendRequest -> UpdateCachedResponse* -> OverwriteCachedResponse
792 // -> PartialHeadersReceived -> NetworkReadCacheWrite*
793 //
794 // 6. HEAD. Not-cached entry:
795 // Pass through. Don't save a HEAD by itself.
796 // Start():
797 // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> SendRequest*
798 //
799 // 7. HEAD. Cached entry, no validation:
800 // Start():
801 // The same flow as for a GET request (example #2)
802 //
803 // Read():
804 // CacheReadData (returns 0)
805 //
806 // 8. HEAD. Cached entry, validation (304):
807 // The request updates the stored headers.
808 // Start(): Same as for a GET request (example #3)
809 //
810 // Read():
811 // CacheReadData (returns 0)
812 //
813 // 9. HEAD. Cached entry, validation and replace (200):
814 // Pass through. The request dooms the old entry, as a HEAD won't be stored by
815 // itself.
816 // Start():
817 // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
818 // CacheReadResponse* -> CacheDispatchValidation ->
819 // BeginPartialCacheValidation() -> BeginCacheValidation() -> SendRequest* ->
820 // SuccessfulSendRequest -> OverwriteCachedResponse -> FinishHeaders*
821 //
822 // 10. HEAD. Sparse entry, partially cached:
823 // Serve the request from the cache, as long as it doesn't require
824 // revalidation. Ignore missing ranges when deciding to revalidate. If the
825 // entry requires revalidation, ignore the whole request and go to full pass
826 // through (the result of the HEAD request will NOT update the entry).
827 //
828 // Start(): Basically the same as example 7, as we never create a partial_
829 // object for this request.
830 //
831 // 11. Prefetch, not-cached entry:
832 // The same as example 1. The "unused_since_prefetch" bit is stored as true in
833 // UpdateCachedResponse.
834 //
835 // 12. Prefetch, cached entry:
836 // Like examples 2-4, only CacheWriteUpdatedPrefetchResponse* is inserted
837 // between CacheReadResponse* and CacheDispatchValidation if the
838 // unused_since_prefetch bit is unset.
839 //
840 // 13. Cached entry less than 5 minutes old, unused_since_prefetch is true:
841 // Skip validation, similar to example 2.
842 // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
843 // CacheReadResponse* -> CacheToggleUnusedSincePrefetch* ->
844 // CacheDispatchValidation -> BeginPartialCacheValidation() ->
845 // BeginCacheValidation() -> ConnectedCallback* -> SetupEntryForRead() ->
846 // FinishHeaders*
847 //
848 // Read():
849 // CacheReadData*
850 //
851 // 14. Cached entry more than 5 minutes old, unused_since_prefetch is true:
852 // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between
853 // CacheReadResponse* and CacheDispatchValidation.
DoLoop(int result)854 int HttpCache::Transaction::DoLoop(int result) {
855 DCHECK_NE(STATE_UNSET, next_state_);
856 DCHECK_NE(STATE_NONE, next_state_);
857 DCHECK(!in_do_loop_);
858
859 int rv = result;
860 State state = next_state_;
861 do {
862 state = next_state_;
863 next_state_ = STATE_UNSET;
864 base::AutoReset<bool> scoped_in_do_loop(&in_do_loop_, true);
865
866 switch (state) {
867 case STATE_GET_BACKEND:
868 DCHECK_EQ(OK, rv);
869 rv = DoGetBackend();
870 break;
871 case STATE_GET_BACKEND_COMPLETE:
872 rv = DoGetBackendComplete(rv);
873 break;
874 case STATE_INIT_ENTRY:
875 DCHECK_EQ(OK, rv);
876 rv = DoInitEntry();
877 break;
878 case STATE_OPEN_OR_CREATE_ENTRY:
879 DCHECK_EQ(OK, rv);
880 rv = DoOpenOrCreateEntry();
881 break;
882 case STATE_OPEN_OR_CREATE_ENTRY_COMPLETE:
883 rv = DoOpenOrCreateEntryComplete(rv);
884 break;
885 case STATE_DOOM_ENTRY:
886 DCHECK_EQ(OK, rv);
887 rv = DoDoomEntry();
888 break;
889 case STATE_DOOM_ENTRY_COMPLETE:
890 rv = DoDoomEntryComplete(rv);
891 break;
892 case STATE_CREATE_ENTRY:
893 DCHECK_EQ(OK, rv);
894 rv = DoCreateEntry();
895 break;
896 case STATE_CREATE_ENTRY_COMPLETE:
897 rv = DoCreateEntryComplete(rv);
898 break;
899 case STATE_ADD_TO_ENTRY:
900 DCHECK_EQ(OK, rv);
901 rv = DoAddToEntry();
902 break;
903 case STATE_ADD_TO_ENTRY_COMPLETE:
904 rv = DoAddToEntryComplete(rv);
905 break;
906 case STATE_DONE_HEADERS_ADD_TO_ENTRY_COMPLETE:
907 rv = DoDoneHeadersAddToEntryComplete(rv);
908 break;
909 case STATE_CACHE_READ_RESPONSE:
910 DCHECK_EQ(OK, rv);
911 rv = DoCacheReadResponse();
912 break;
913 case STATE_CACHE_READ_RESPONSE_COMPLETE:
914 rv = DoCacheReadResponseComplete(rv);
915 break;
916 case STATE_WRITE_UPDATED_PREFETCH_RESPONSE:
917 DCHECK_EQ(OK, rv);
918 rv = DoCacheWriteUpdatedPrefetchResponse(rv);
919 break;
920 case STATE_WRITE_UPDATED_PREFETCH_RESPONSE_COMPLETE:
921 rv = DoCacheWriteUpdatedPrefetchResponseComplete(rv);
922 break;
923 case STATE_CACHE_DISPATCH_VALIDATION:
924 DCHECK_EQ(OK, rv);
925 rv = DoCacheDispatchValidation();
926 break;
927 case STATE_CACHE_QUERY_DATA:
928 DCHECK_EQ(OK, rv);
929 rv = DoCacheQueryData();
930 break;
931 case STATE_CACHE_QUERY_DATA_COMPLETE:
932 rv = DoCacheQueryDataComplete(rv);
933 break;
934 case STATE_START_PARTIAL_CACHE_VALIDATION:
935 DCHECK_EQ(OK, rv);
936 rv = DoStartPartialCacheValidation();
937 break;
938 case STATE_COMPLETE_PARTIAL_CACHE_VALIDATION:
939 rv = DoCompletePartialCacheValidation(rv);
940 break;
941 case STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT:
942 DCHECK_EQ(OK, rv);
943 rv = DoCacheUpdateStaleWhileRevalidateTimeout();
944 break;
945 case STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT_COMPLETE:
946 rv = DoCacheUpdateStaleWhileRevalidateTimeoutComplete(rv);
947 break;
948 case STATE_CONNECTED_CALLBACK:
949 rv = DoConnectedCallback();
950 break;
951 case STATE_CONNECTED_CALLBACK_COMPLETE:
952 rv = DoConnectedCallbackComplete(rv);
953 break;
954 case STATE_SETUP_ENTRY_FOR_READ:
955 DCHECK_EQ(OK, rv);
956 rv = DoSetupEntryForRead();
957 break;
958 case STATE_SEND_REQUEST:
959 DCHECK_EQ(OK, rv);
960 rv = DoSendRequest();
961 break;
962 case STATE_SEND_REQUEST_COMPLETE:
963 rv = DoSendRequestComplete(rv);
964 break;
965 case STATE_SUCCESSFUL_SEND_REQUEST:
966 DCHECK_EQ(OK, rv);
967 rv = DoSuccessfulSendRequest();
968 break;
969 case STATE_UPDATE_CACHED_RESPONSE:
970 DCHECK_EQ(OK, rv);
971 rv = DoUpdateCachedResponse();
972 break;
973 case STATE_CACHE_WRITE_UPDATED_RESPONSE:
974 DCHECK_EQ(OK, rv);
975 rv = DoCacheWriteUpdatedResponse();
976 break;
977 case STATE_CACHE_WRITE_UPDATED_RESPONSE_COMPLETE:
978 rv = DoCacheWriteUpdatedResponseComplete(rv);
979 break;
980 case STATE_UPDATE_CACHED_RESPONSE_COMPLETE:
981 rv = DoUpdateCachedResponseComplete(rv);
982 break;
983 case STATE_OVERWRITE_CACHED_RESPONSE:
984 DCHECK_EQ(OK, rv);
985 rv = DoOverwriteCachedResponse();
986 break;
987 case STATE_CACHE_WRITE_RESPONSE:
988 DCHECK_EQ(OK, rv);
989 rv = DoCacheWriteResponse();
990 break;
991 case STATE_CACHE_WRITE_RESPONSE_COMPLETE:
992 rv = DoCacheWriteResponseComplete(rv);
993 break;
994 case STATE_TRUNCATE_CACHED_DATA:
995 DCHECK_EQ(OK, rv);
996 rv = DoTruncateCachedData();
997 break;
998 case STATE_TRUNCATE_CACHED_DATA_COMPLETE:
999 rv = DoTruncateCachedDataComplete(rv);
1000 break;
1001 case STATE_PARTIAL_HEADERS_RECEIVED:
1002 DCHECK_EQ(OK, rv);
1003 rv = DoPartialHeadersReceived();
1004 break;
1005 case STATE_HEADERS_PHASE_CANNOT_PROCEED:
1006 rv = DoHeadersPhaseCannotProceed(rv);
1007 break;
1008 case STATE_FINISH_HEADERS:
1009 rv = DoFinishHeaders(rv);
1010 break;
1011 case STATE_FINISH_HEADERS_COMPLETE:
1012 rv = DoFinishHeadersComplete(rv);
1013 break;
1014 case STATE_NETWORK_READ_CACHE_WRITE:
1015 DCHECK_EQ(OK, rv);
1016 rv = DoNetworkReadCacheWrite();
1017 break;
1018 case STATE_NETWORK_READ_CACHE_WRITE_COMPLETE:
1019 rv = DoNetworkReadCacheWriteComplete(rv);
1020 break;
1021 case STATE_CACHE_READ_DATA:
1022 DCHECK_EQ(OK, rv);
1023 rv = DoCacheReadData();
1024 break;
1025 case STATE_CACHE_READ_DATA_COMPLETE:
1026 rv = DoCacheReadDataComplete(rv);
1027 break;
1028 case STATE_NETWORK_READ:
1029 DCHECK_EQ(OK, rv);
1030 rv = DoNetworkRead();
1031 break;
1032 case STATE_NETWORK_READ_COMPLETE:
1033 rv = DoNetworkReadComplete(rv);
1034 break;
1035 default:
1036 NOTREACHED() << "bad state " << state;
1037 rv = ERR_FAILED;
1038 break;
1039 }
1040 DCHECK(next_state_ != STATE_UNSET) << "Previous state was " << state;
1041
1042 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
1043
1044 // Assert Start() state machine's allowed last state in successful cases when
1045 // caching is happening.
1046 DCHECK(reading_ || rv != OK || !entry_ ||
1047 state == STATE_FINISH_HEADERS_COMPLETE);
1048
1049 if (rv != ERR_IO_PENDING && !callback_.is_null()) {
1050 read_buf_ = nullptr; // Release the buffer before invoking the callback.
1051 std::move(callback_).Run(rv);
1052 }
1053
1054 return rv;
1055 }
1056
DoGetBackend()1057 int HttpCache::Transaction::DoGetBackend() {
1058 cache_pending_ = true;
1059 TransitionToState(STATE_GET_BACKEND_COMPLETE);
1060 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_GET_BACKEND);
1061 return cache_->GetBackendForTransaction(this);
1062 }
1063
DoGetBackendComplete(int result)1064 int HttpCache::Transaction::DoGetBackendComplete(int result) {
1065 DCHECK(result == OK || result == ERR_FAILED);
1066 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_GET_BACKEND,
1067 result);
1068 cache_pending_ = false;
1069
1070 // Reset mode_ that might get set in this function. This is done because this
1071 // function can be invoked multiple times for a transaction.
1072 mode_ = NONE;
1073 const bool should_pass_through = ShouldPassThrough();
1074
1075 if (!should_pass_through) {
1076 cache_key_ = *cache_->GenerateCacheKeyForRequest(request_);
1077
1078 // Requested cache access mode.
1079 if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
1080 if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
1081 // The client has asked for nonsense.
1082 TransitionToState(STATE_FINISH_HEADERS);
1083 return ERR_CACHE_MISS;
1084 }
1085 mode_ = READ;
1086 } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
1087 mode_ = WRITE;
1088 } else {
1089 mode_ = READ_WRITE;
1090 }
1091
1092 // Downgrade to UPDATE if the request has been externally conditionalized.
1093 if (external_validation_.initialized) {
1094 if (mode_ & WRITE) {
1095 // Strip off the READ_DATA bit (and maybe add back a READ_META bit
1096 // in case READ was off).
1097 mode_ = UPDATE;
1098 } else {
1099 mode_ = NONE;
1100 }
1101 }
1102 }
1103
1104 // Use PUT, DELETE, and PATCH only to invalidate existing stored entries.
1105 if ((method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH") &&
1106 mode_ != READ_WRITE && mode_ != WRITE) {
1107 mode_ = NONE;
1108 }
1109
1110 // Note that if mode_ == UPDATE (which is tied to external_validation_), the
1111 // transaction behaves the same for GET and HEAD requests at this point: if it
1112 // was not modified, the entry is updated and a response is not returned from
1113 // the cache. If we receive 200, it doesn't matter if there was a validation
1114 // header or not.
1115 if (method_ == "HEAD" && mode_ == WRITE) {
1116 mode_ = NONE;
1117 }
1118
1119 // If must use cache, then we must fail. This can happen for back/forward
1120 // navigations to a page generated via a form post.
1121 if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
1122 TransitionToState(STATE_FINISH_HEADERS);
1123 return ERR_CACHE_MISS;
1124 }
1125
1126 if (mode_ == NONE) {
1127 if (partial_) {
1128 partial_->RestoreHeaders(&custom_request_->extra_headers);
1129 partial_.reset();
1130 }
1131 TransitionToState(STATE_SEND_REQUEST);
1132 } else {
1133 TransitionToState(STATE_INIT_ENTRY);
1134 }
1135
1136 // This is only set if we have something to do with the response.
1137 range_requested_ = (partial_.get() != nullptr);
1138
1139 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoGetBackendComplete",
1140 perfetto::Track(trace_id_), "mode", mode_,
1141 "should_pass_through", should_pass_through);
1142 return OK;
1143 }
1144
DoInitEntry()1145 int HttpCache::Transaction::DoInitEntry() {
1146 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoInitEntry",
1147 perfetto::Track(trace_id_));
1148 DCHECK(!new_entry_);
1149
1150 if (!cache_.get()) {
1151 TransitionToState(STATE_FINISH_HEADERS);
1152 return ERR_UNEXPECTED;
1153 }
1154
1155 if (mode_ == WRITE) {
1156 TransitionToState(STATE_DOOM_ENTRY);
1157 return OK;
1158 }
1159
1160 TransitionToState(STATE_OPEN_OR_CREATE_ENTRY);
1161 return OK;
1162 }
1163
DoOpenOrCreateEntry()1164 int HttpCache::Transaction::DoOpenOrCreateEntry() {
1165 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoOpenOrCreateEntry",
1166 perfetto::Track(trace_id_));
1167 DCHECK(!new_entry_);
1168 TransitionToState(STATE_OPEN_OR_CREATE_ENTRY_COMPLETE);
1169 cache_pending_ = true;
1170 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_OPEN_OR_CREATE_ENTRY);
1171 first_cache_access_since_ = TimeTicks::Now();
1172 const bool has_opened_or_created_entry = has_opened_or_created_entry_;
1173 has_opened_or_created_entry_ = true;
1174 record_entry_open_or_creation_time_ = false;
1175
1176 // See if we already have something working with this cache key.
1177 new_entry_ = cache_->GetActiveEntry(cache_key_);
1178 if (new_entry_) {
1179 return OK;
1180 }
1181
1182 // See if we could potentially doom the entry based on hints the backend keeps
1183 // in memory.
1184 // Currently only SimpleCache utilizes in memory hints. If an entry is found
1185 // unsuitable, and thus Doomed, SimpleCache can also optimize the
1186 // OpenOrCreateEntry() call to reduce the overhead of trying to open an entry
1187 // we know is doomed.
1188 uint8_t in_memory_info =
1189 cache_->GetCurrentBackend()->GetEntryInMemoryData(cache_key_);
1190 bool entry_not_suitable = false;
1191 if (MaybeRejectBasedOnEntryInMemoryData(in_memory_info)) {
1192 cache_->GetCurrentBackend()->DoomEntry(cache_key_, priority_,
1193 base::DoNothing());
1194 entry_not_suitable = true;
1195 // Documents the case this applies in
1196 DCHECK_EQ(mode_, READ_WRITE);
1197 // Record this as CantConditionalize, but otherwise proceed as we would
1198 // below --- as we've already dropped the old entry.
1199 couldnt_conditionalize_request_ = true;
1200 validation_cause_ = VALIDATION_CAUSE_ZERO_FRESHNESS;
1201 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE);
1202 }
1203
1204 if (!has_opened_or_created_entry) {
1205 record_entry_open_or_creation_time_ = true;
1206 }
1207
1208 // mode_ can be anything but NONE or WRITE at this point (READ, UPDATE, or
1209 // READ_WRITE).
1210 // READ, UPDATE, certain READ_WRITEs, and some methods shouldn't create, so
1211 // try only opening.
1212 if (mode_ != READ_WRITE || ShouldOpenOnlyMethods()) {
1213 if (entry_not_suitable) {
1214 // The entry isn't suitable and we can't create a new one.
1215 return net::ERR_CACHE_ENTRY_NOT_SUITABLE;
1216 }
1217
1218 return cache_->OpenEntry(cache_key_, &new_entry_, this);
1219 }
1220
1221 return cache_->OpenOrCreateEntry(cache_key_, &new_entry_, this);
1222 }
1223
DoOpenOrCreateEntryComplete(int result)1224 int HttpCache::Transaction::DoOpenOrCreateEntryComplete(int result) {
1225 TRACE_EVENT_INSTANT(
1226 "net", "HttpCacheTransaction::DoOpenOrCreateEntryComplete",
1227 perfetto::Track(trace_id_), "result",
1228 (result == OK ? (new_entry_->opened() ? "opened" : "created")
1229 : "failed"));
1230
1231 const bool record_uma =
1232 record_entry_open_or_creation_time_ && cache_ &&
1233 cache_->GetCurrentBackend() &&
1234 cache_->GetCurrentBackend()->GetCacheType() != MEMORY_CACHE;
1235 record_entry_open_or_creation_time_ = false;
1236
1237 // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
1238 // OK, otherwise the cache will end up with an active entry without any
1239 // transaction attached.
1240 net_log_.EndEvent(NetLogEventType::HTTP_CACHE_OPEN_OR_CREATE_ENTRY, [&] {
1241 base::Value::Dict params;
1242 if (result == OK) {
1243 params.Set("result", new_entry_->opened() ? "opened" : "created");
1244 } else {
1245 params.Set("net_error", result);
1246 }
1247 return params;
1248 });
1249
1250 cache_pending_ = false;
1251
1252 if (result == OK) {
1253 if (new_entry_->opened()) {
1254 if (record_uma) {
1255 base::UmaHistogramTimes(
1256 "HttpCache.OpenDiskEntry",
1257 base::TimeTicks::Now() - first_cache_access_since_);
1258 }
1259 } else {
1260 if (record_uma) {
1261 base::UmaHistogramTimes(
1262 "HttpCache.CreateDiskEntry",
1263 base::TimeTicks::Now() - first_cache_access_since_);
1264 }
1265
1266 // Entry was created so mode changes to WRITE.
1267 mode_ = WRITE;
1268 }
1269
1270 TransitionToState(STATE_ADD_TO_ENTRY);
1271 return OK;
1272 }
1273
1274 if (result == ERR_CACHE_RACE) {
1275 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
1276 return OK;
1277 }
1278
1279 // No need to explicitly handle ERR_CACHE_ENTRY_NOT_SUITABLE as the
1280 // ShouldOpenOnlyMethods() check will handle it.
1281
1282 if (mode_ & WRITE) {
1283 // We were unable to open or create an entry.
1284 DLOG(WARNING) << "Unable to open or create cache entry";
1285 }
1286
1287 if (ShouldOpenOnlyMethods()) {
1288 // These methods, on failure, should bypass the cache.
1289 mode_ = NONE;
1290 TransitionToState(STATE_SEND_REQUEST);
1291 return OK;
1292 }
1293
1294 // Since the operation failed, what we do next depends on the mode_ which can
1295 // be the following: READ, READ_WRITE, or UPDATE. Note: mode_ cannot be WRITE
1296 // or NONE at this point as DoInitEntry() handled those cases.
1297
1298 switch (mode_) {
1299 case READ:
1300 // The entry does not exist, and we are not permitted to create a new
1301 // entry, so we must fail.
1302 TransitionToState(STATE_FINISH_HEADERS);
1303 return ERR_CACHE_MISS;
1304 case READ_WRITE:
1305 // Unable to open or create; set the mode to NONE in order to bypass the
1306 // cache entry and read from the network directly.
1307 mode_ = NONE;
1308 if (partial_) {
1309 partial_->RestoreHeaders(&custom_request_->extra_headers);
1310 }
1311 TransitionToState(STATE_SEND_REQUEST);
1312 break;
1313 case UPDATE:
1314 // There is no cache entry to update; proceed without caching.
1315 DCHECK(!partial_);
1316 mode_ = NONE;
1317 TransitionToState(STATE_SEND_REQUEST);
1318 break;
1319 default:
1320 NOTREACHED();
1321 }
1322
1323 return OK;
1324 }
1325
DoDoomEntry()1326 int HttpCache::Transaction::DoDoomEntry() {
1327 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoDoomEntry",
1328 perfetto::Track(trace_id_));
1329 TransitionToState(STATE_DOOM_ENTRY_COMPLETE);
1330 cache_pending_ = true;
1331 if (first_cache_access_since_.is_null()) {
1332 first_cache_access_since_ = TimeTicks::Now();
1333 }
1334 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_DOOM_ENTRY);
1335 return cache_->DoomEntry(cache_key_, this);
1336 }
1337
DoDoomEntryComplete(int result)1338 int HttpCache::Transaction::DoDoomEntryComplete(int result) {
1339 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoDoomEntryComplete",
1340 perfetto::Track(trace_id_), "result", result);
1341 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_DOOM_ENTRY,
1342 result);
1343 cache_pending_ = false;
1344 TransitionToState(result == ERR_CACHE_RACE
1345 ? STATE_HEADERS_PHASE_CANNOT_PROCEED
1346 : STATE_CREATE_ENTRY);
1347 return OK;
1348 }
1349
DoCreateEntry()1350 int HttpCache::Transaction::DoCreateEntry() {
1351 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoCreateEntry",
1352 perfetto::Track(trace_id_));
1353 DCHECK(!new_entry_);
1354 TransitionToState(STATE_CREATE_ENTRY_COMPLETE);
1355 cache_pending_ = true;
1356 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_CREATE_ENTRY);
1357 return cache_->CreateEntry(cache_key_, &new_entry_, this);
1358 }
1359
DoCreateEntryComplete(int result)1360 int HttpCache::Transaction::DoCreateEntryComplete(int result) {
1361 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoCreateEntryComplete",
1362 perfetto::Track(trace_id_), "result", result);
1363 // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
1364 // OK, otherwise the cache will end up with an active entry without any
1365 // transaction attached.
1366 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_CREATE_ENTRY,
1367 result);
1368 cache_pending_ = false;
1369 switch (result) {
1370 case OK:
1371 TransitionToState(STATE_ADD_TO_ENTRY);
1372 break;
1373
1374 case ERR_CACHE_RACE:
1375 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
1376 break;
1377
1378 default:
1379 DLOG(WARNING) << "Unable to create cache entry";
1380
1381 // Set the mode to NONE in order to bypass the cache entry and read from
1382 // the network directly.
1383 mode_ = NONE;
1384 if (!done_headers_create_new_entry_) {
1385 if (partial_) {
1386 partial_->RestoreHeaders(&custom_request_->extra_headers);
1387 }
1388 TransitionToState(STATE_SEND_REQUEST);
1389 return OK;
1390 }
1391 // The headers have already been received as a result of validation,
1392 // triggering the doom of the old entry. So no network request needs to
1393 // be sent. Note that since mode_ is NONE, the response won't be written
1394 // to cache. Transition to STATE_CACHE_WRITE_RESPONSE as that's the state
1395 // the transaction left off on when it tried to create the new entry.
1396 done_headers_create_new_entry_ = false;
1397 TransitionToState(STATE_CACHE_WRITE_RESPONSE);
1398 }
1399 return OK;
1400 }
1401
DoAddToEntry()1402 int HttpCache::Transaction::DoAddToEntry() {
1403 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoAddToEntry",
1404 perfetto::Track(trace_id_));
1405 DCHECK(new_entry_);
1406 cache_pending_ = true;
1407 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY);
1408 DCHECK(entry_lock_waiting_since_.is_null());
1409
1410 // By this point whether the entry was created or opened is no longer relevant
1411 // for this transaction. However there may be queued transactions that want to
1412 // use this entry and from their perspective the entry was opened, so change
1413 // the flag to reflect that.
1414 new_entry_->set_opened(true);
1415
1416 int rv = cache_->AddTransactionToEntry(new_entry_, this);
1417 CHECK_EQ(rv, ERR_IO_PENDING);
1418
1419 // If headers phase is already done then we are here because of validation not
1420 // matching and creating a new entry. This transaction should be the
1421 // first transaction of that new entry and thus it will not have cache lock
1422 // delays, thus returning early from here.
1423 if (done_headers_create_new_entry_) {
1424 DCHECK_EQ(mode_, WRITE);
1425 TransitionToState(STATE_DONE_HEADERS_ADD_TO_ENTRY_COMPLETE);
1426 return rv;
1427 }
1428
1429 TransitionToState(STATE_ADD_TO_ENTRY_COMPLETE);
1430
1431 // For a very-select case of creating a new non-range request entry, run the
1432 // AddTransactionToEntry in parallel with sending the network request to
1433 // hide the latency. This will run until the next ERR_IO_PENDING (or
1434 // failure).
1435 if (!partial_ && mode_ == WRITE) {
1436 CHECK(!waiting_for_cache_io_);
1437 waiting_for_cache_io_ = true;
1438 rv = OK;
1439 }
1440
1441 entry_lock_waiting_since_ = TimeTicks::Now();
1442 AddCacheLockTimeoutHandler(new_entry_.get());
1443 return rv;
1444 }
1445
AddCacheLockTimeoutHandler(ActiveEntry * entry)1446 void HttpCache::Transaction::AddCacheLockTimeoutHandler(ActiveEntry* entry) {
1447 CHECK(next_state_ == STATE_ADD_TO_ENTRY_COMPLETE ||
1448 next_state_ == STATE_FINISH_HEADERS_COMPLETE);
1449 if ((bypass_lock_for_test_ && next_state_ == STATE_ADD_TO_ENTRY_COMPLETE) ||
1450 (bypass_lock_after_headers_for_test_ &&
1451 next_state_ == STATE_FINISH_HEADERS_COMPLETE)) {
1452 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1453 FROM_HERE,
1454 base::BindOnce(&HttpCache::Transaction::OnCacheLockTimeout,
1455 weak_factory_.GetWeakPtr(), entry_lock_waiting_since_));
1456 } else {
1457 int timeout_milliseconds = 20 * 1000;
1458 if (partial_ && entry->HasWriters() && !entry->writers()->IsEmpty() &&
1459 entry->writers()->IsExclusive()) {
1460 // Even though entry_->writers takes care of allowing multiple writers to
1461 // simultaneously govern reading from the network and writing to the cache
1462 // for full requests, partial requests are still blocked by the
1463 // reader/writer lock.
1464 // Bypassing the cache after 25 ms of waiting for the cache lock
1465 // eliminates a long running issue, http://crbug.com/31014, where
1466 // two of the same media resources could not be played back simultaneously
1467 // due to one locking the cache entry until the entire video was
1468 // downloaded.
1469 // Bypassing the cache is not ideal, as we are now ignoring the cache
1470 // entirely for all range requests to a resource beyond the first. This
1471 // is however a much more succinct solution than the alternatives, which
1472 // would require somewhat significant changes to the http caching logic.
1473 //
1474 // Allow some timeout slack for the entry addition to complete in case
1475 // the writer lock is imminently released; we want to avoid skipping
1476 // the cache if at all possible. See http://crbug.com/408765
1477 timeout_milliseconds = 25;
1478 }
1479 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
1480 FROM_HERE,
1481 base::BindOnce(&HttpCache::Transaction::OnCacheLockTimeout,
1482 weak_factory_.GetWeakPtr(), entry_lock_waiting_since_),
1483 base::Milliseconds(timeout_milliseconds));
1484 }
1485 }
1486
DoAddToEntryComplete(int result)1487 int HttpCache::Transaction::DoAddToEntryComplete(int result) {
1488 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoAddToEntryComplete",
1489 perfetto::Track(trace_id_), "result", result);
1490 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY,
1491 result);
1492 if (cache_ && cache_->GetCurrentBackend() &&
1493 cache_->GetCurrentBackend()->GetCacheType() != MEMORY_CACHE) {
1494 const base::TimeDelta entry_lock_wait =
1495 TimeTicks::Now() - entry_lock_waiting_since_;
1496 base::UmaHistogramTimes("HttpCache.AddTransactionToEntry", entry_lock_wait);
1497 }
1498
1499 DCHECK(new_entry_);
1500
1501 if (!waiting_for_cache_io_) {
1502 entry_lock_waiting_since_ = TimeTicks();
1503 cache_pending_ = false;
1504
1505 if (result == OK) {
1506 entry_ = std::move(new_entry_);
1507 }
1508
1509 // If there is a failure, the cache should have taken care of new_entry_.
1510 new_entry_.reset();
1511 }
1512
1513 if (result == ERR_CACHE_RACE) {
1514 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
1515 return OK;
1516 }
1517
1518 if (result == ERR_CACHE_LOCK_TIMEOUT) {
1519 if (mode_ == READ) {
1520 TransitionToState(STATE_FINISH_HEADERS);
1521 return ERR_CACHE_MISS;
1522 }
1523
1524 // The cache is busy, bypass it for this transaction.
1525 mode_ = NONE;
1526 TransitionToState(STATE_SEND_REQUEST);
1527 if (partial_) {
1528 partial_->RestoreHeaders(&custom_request_->extra_headers);
1529 partial_.reset();
1530 }
1531 return OK;
1532 }
1533
1534 // TODO(crbug.com/713354) Access timestamp for histograms only if entry is
1535 // already written, to avoid data race since cache thread can also access
1536 // this.
1537 if (entry_ && !entry_->IsWritingInProgress()) {
1538 open_entry_last_used_ = entry_->GetEntry()->GetLastUsed();
1539 }
1540
1541 // TODO(jkarlin): We should either handle the case or DCHECK.
1542 if (result != OK) {
1543 NOTREACHED();
1544 TransitionToState(STATE_FINISH_HEADERS);
1545 return result;
1546 }
1547
1548 if (mode_ == WRITE) {
1549 if (partial_) {
1550 partial_->RestoreHeaders(&custom_request_->extra_headers);
1551 }
1552 TransitionToState(STATE_SEND_REQUEST);
1553 } else {
1554 // We have to read the headers from the cached entry.
1555 DCHECK(mode_ & READ_META);
1556 TransitionToState(STATE_CACHE_READ_RESPONSE);
1557 }
1558 return OK;
1559 }
1560
DoDoneHeadersAddToEntryComplete(int result)1561 int HttpCache::Transaction::DoDoneHeadersAddToEntryComplete(int result) {
1562 TRACE_EVENT_INSTANT("net",
1563 "HttpCacheTransaction::DoDoneHeadersAddToEntryComplete",
1564 perfetto::Track(trace_id_), "result", result);
1565 // This transaction's response headers did not match its ActiveEntry so it
1566 // created a new ActiveEntry (new_entry_) to write to (and doomed the old
1567 // one). Now that the new entry has been created, start writing the response.
1568
1569 DCHECK_EQ(result, OK);
1570 DCHECK_EQ(mode_, WRITE);
1571 DCHECK(new_entry_);
1572 DCHECK(response_.headers);
1573
1574 cache_pending_ = false;
1575 done_headers_create_new_entry_ = false;
1576
1577 // It is unclear exactly how this state is reached with an ERR_CACHE_RACE, but
1578 // this check appears to fix a rare crash. See crbug.com/959194.
1579 if (result == ERR_CACHE_RACE) {
1580 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
1581 return OK;
1582 }
1583
1584 entry_ = std::move(new_entry_);
1585 DCHECK_NE(response_.headers->response_code(), net::HTTP_NOT_MODIFIED);
1586 DCHECK(entry_->CanTransactionWriteResponseHeaders(this, partial_ != nullptr,
1587 false));
1588 TransitionToState(STATE_CACHE_WRITE_RESPONSE);
1589 return OK;
1590 }
1591
DoCacheReadResponse()1592 int HttpCache::Transaction::DoCacheReadResponse() {
1593 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoCacheReadResponse",
1594 perfetto::Track(trace_id_));
1595 DCHECK(entry_);
1596 TransitionToState(STATE_CACHE_READ_RESPONSE_COMPLETE);
1597
1598 io_buf_len_ = entry_->GetEntry()->GetDataSize(kResponseInfoIndex);
1599 read_buf_ = base::MakeRefCounted<IOBufferWithSize>(io_buf_len_);
1600
1601 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_READ_INFO);
1602 BeginDiskCacheAccessTimeCount();
1603 return entry_->GetEntry()->ReadData(kResponseInfoIndex, 0, read_buf_.get(),
1604 io_buf_len_, io_callback_);
1605 }
1606
DoCacheReadResponseComplete(int result)1607 int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
1608 TRACE_EVENT_INSTANT(
1609 "net", "HttpCacheTransaction::DoCacheReadResponseComplete",
1610 perfetto::Track(trace_id_), "result", result, "io_buf_len", io_buf_len_);
1611 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_READ_INFO,
1612 result);
1613 EndDiskCacheAccessTimeCount(DiskCacheAccessType::kRead);
1614
1615 // Record the time immediately before the cached response is parsed.
1616 read_headers_since_ = TimeTicks::Now();
1617
1618 if (result != io_buf_len_ ||
1619 !HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_, &response_,
1620 &truncated_)) {
1621 return OnCacheReadError(result, true);
1622 }
1623
1624 // If the read response matches the clearing filter of FPS, doom the entry
1625 // and restart transaction.
1626 if (ShouldByPassCacheForFirstPartySets(initial_request_->fps_cache_filter,
1627 response_.browser_run_id)) {
1628 result = ERR_CACHE_ENTRY_NOT_SUITABLE;
1629 return OnCacheReadError(result, true);
1630 }
1631
1632 // TODO(crbug.com/713354) Only get data size if there is no other transaction
1633 // currently writing the response body due to the data race mentioned in the
1634 // associated bug.
1635 if (!entry_->IsWritingInProgress()) {
1636 int current_size = entry_->GetEntry()->GetDataSize(kResponseContentIndex);
1637 int64_t full_response_length = response_.headers->GetContentLength();
1638
1639 // Some resources may have slipped in as truncated when they're not.
1640 if (full_response_length == current_size) {
1641 truncated_ = false;
1642 }
1643
1644 // The state machine's handling of StopCaching unfortunately doesn't deal
1645 // well with resources that are larger than 2GB when there is a truncated or
1646 // sparse cache entry. While the state machine is reworked to resolve this,
1647 // the following logic is put in place to defer such requests to the
1648 // network. The cache should not be storing multi gigabyte resources. See
1649 // http://crbug.com/89567.
1650 if ((truncated_ ||
1651 response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT) &&
1652 !range_requested_ &&
1653 full_response_length > std::numeric_limits<int32_t>::max()) {
1654 DCHECK(!partial_);
1655
1656 // Doom the entry so that no other transaction gets added to this entry
1657 // and avoid a race of not being able to check this condition because
1658 // writing is in progress.
1659 DoneWithEntry(false);
1660 TransitionToState(STATE_SEND_REQUEST);
1661 return OK;
1662 }
1663 }
1664
1665 if (response_.restricted_prefetch &&
1666 !(request_->load_flags & LOAD_CAN_USE_RESTRICTED_PREFETCH)) {
1667 TransitionToState(STATE_SEND_REQUEST);
1668 return OK;
1669 }
1670
1671 // When a restricted prefetch is reused, we lift its reuse restriction.
1672 bool restricted_prefetch_reuse =
1673 response_.restricted_prefetch &&
1674 request_->load_flags & LOAD_CAN_USE_RESTRICTED_PREFETCH;
1675 DCHECK(!restricted_prefetch_reuse || response_.unused_since_prefetch);
1676
1677 if (response_.unused_since_prefetch !=
1678 !!(request_->load_flags & LOAD_PREFETCH)) {
1679 // Either this is the first use of an entry since it was prefetched XOR
1680 // this is a prefetch. The value of response.unused_since_prefetch is
1681 // valid for this transaction but the bit needs to be flipped in storage.
1682 DCHECK(!updated_prefetch_response_);
1683 updated_prefetch_response_ = std::make_unique<HttpResponseInfo>(response_);
1684 updated_prefetch_response_->unused_since_prefetch =
1685 !response_.unused_since_prefetch;
1686 if (response_.restricted_prefetch &&
1687 request_->load_flags & LOAD_CAN_USE_RESTRICTED_PREFETCH) {
1688 updated_prefetch_response_->restricted_prefetch = false;
1689 }
1690
1691 TransitionToState(STATE_WRITE_UPDATED_PREFETCH_RESPONSE);
1692 return OK;
1693 }
1694
1695 TransitionToState(STATE_CACHE_DISPATCH_VALIDATION);
1696 return OK;
1697 }
1698
DoCacheWriteUpdatedPrefetchResponse(int result)1699 int HttpCache::Transaction::DoCacheWriteUpdatedPrefetchResponse(int result) {
1700 TRACE_EVENT_INSTANT(
1701 "net", "HttpCacheTransaction::DoCacheWriteUpdatedPrefetchResponse",
1702 perfetto::Track(trace_id_), "result", result);
1703 DCHECK(updated_prefetch_response_);
1704 // TODO(jkarlin): If DoUpdateCachedResponse is also called for this
1705 // transaction then metadata will be written to cache twice. If prefetching
1706 // becomes more common, consider combining the writes.
1707 TransitionToState(STATE_WRITE_UPDATED_PREFETCH_RESPONSE_COMPLETE);
1708 return WriteResponseInfoToEntry(*updated_prefetch_response_.get(),
1709 truncated_);
1710 }
1711
DoCacheWriteUpdatedPrefetchResponseComplete(int result)1712 int HttpCache::Transaction::DoCacheWriteUpdatedPrefetchResponseComplete(
1713 int result) {
1714 TRACE_EVENT_INSTANT(
1715 "net",
1716 "HttpCacheTransaction::DoCacheWriteUpdatedPrefetchResponseComplete",
1717 perfetto::Track(trace_id_), "result", result);
1718 updated_prefetch_response_.reset();
1719 TransitionToState(STATE_CACHE_DISPATCH_VALIDATION);
1720 return OnWriteResponseInfoToEntryComplete(result);
1721 }
1722
DoCacheDispatchValidation()1723 int HttpCache::Transaction::DoCacheDispatchValidation() {
1724 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoCacheDispatchValidation",
1725 perfetto::Track(trace_id_));
1726 if (!entry_) {
1727 // Entry got destroyed when twiddling unused-since-prefetch bit.
1728 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
1729 return OK;
1730 }
1731
1732 // We now have access to the cache entry.
1733 //
1734 // o if we are a reader for the transaction, then we can start reading the
1735 // cache entry.
1736 //
1737 // o if we can read or write, then we should check if the cache entry needs
1738 // to be validated and then issue a network request if needed or just read
1739 // from the cache if the cache entry is already valid.
1740 //
1741 // o if we are set to UPDATE, then we are handling an externally
1742 // conditionalized request (if-modified-since / if-none-match). We check
1743 // if the request headers define a validation request.
1744 //
1745 int result = ERR_FAILED;
1746 switch (mode_) {
1747 case READ:
1748 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED);
1749 result = BeginCacheRead();
1750 break;
1751 case READ_WRITE:
1752 result = BeginPartialCacheValidation();
1753 break;
1754 case UPDATE:
1755 result = BeginExternallyConditionalizedRequest();
1756 break;
1757 case WRITE:
1758 default:
1759 NOTREACHED();
1760 }
1761 return result;
1762 }
1763
DoCacheQueryData()1764 int HttpCache::Transaction::DoCacheQueryData() {
1765 TransitionToState(STATE_CACHE_QUERY_DATA_COMPLETE);
1766 return entry_->GetEntry()->ReadyForSparseIO(io_callback_);
1767 }
1768
DoCacheQueryDataComplete(int result)1769 int HttpCache::Transaction::DoCacheQueryDataComplete(int result) {
1770 DCHECK_EQ(OK, result);
1771 if (!cache_.get()) {
1772 TransitionToState(STATE_FINISH_HEADERS);
1773 return ERR_UNEXPECTED;
1774 }
1775
1776 return ValidateEntryHeadersAndContinue();
1777 }
1778
1779 // We may end up here multiple times for a given request.
DoStartPartialCacheValidation()1780 int HttpCache::Transaction::DoStartPartialCacheValidation() {
1781 if (mode_ == NONE) {
1782 TransitionToState(STATE_FINISH_HEADERS);
1783 return OK;
1784 }
1785
1786 TransitionToState(STATE_COMPLETE_PARTIAL_CACHE_VALIDATION);
1787 return partial_->ShouldValidateCache(entry_->GetEntry(), io_callback_);
1788 }
1789
DoCompletePartialCacheValidation(int result)1790 int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) {
1791 if (!result && reading_) {
1792 // This is the end of the request.
1793 DoneWithEntry(true);
1794 TransitionToState(STATE_FINISH_HEADERS);
1795 return result;
1796 }
1797
1798 if (result < 0) {
1799 TransitionToState(STATE_FINISH_HEADERS);
1800 return result;
1801 }
1802
1803 partial_->PrepareCacheValidation(entry_->GetEntry(),
1804 &custom_request_->extra_headers);
1805
1806 if (reading_ && partial_->IsCurrentRangeCached()) {
1807 // We're about to read a range of bytes from the cache. Signal it to the
1808 // consumer through the "connected" callback.
1809 TransitionToState(STATE_CONNECTED_CALLBACK);
1810 return OK;
1811 }
1812
1813 return BeginCacheValidation();
1814 }
1815
DoCacheUpdateStaleWhileRevalidateTimeout()1816 int HttpCache::Transaction::DoCacheUpdateStaleWhileRevalidateTimeout() {
1817 TRACE_EVENT_INSTANT(
1818 "net", "HttpCacheTransaction::DoCacheUpdateStaleWhileRevalidateTimeout",
1819 perfetto::Track(trace_id_));
1820 response_.stale_revalidate_timeout =
1821 cache_->clock_->Now() + kStaleRevalidateTimeout;
1822 TransitionToState(STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT_COMPLETE);
1823
1824 // We shouldn't be using stale truncated entries; if we did, the false below
1825 // would be wrong.
1826 DCHECK(!truncated_);
1827 return WriteResponseInfoToEntry(response_, false);
1828 }
1829
DoCacheUpdateStaleWhileRevalidateTimeoutComplete(int result)1830 int HttpCache::Transaction::DoCacheUpdateStaleWhileRevalidateTimeoutComplete(
1831 int result) {
1832 TRACE_EVENT_INSTANT(
1833 "net",
1834 "HttpCacheTransaction::DoCacheUpdateStaleWhileRevalidateTimeoutComplete",
1835 perfetto::Track(trace_id_), "result", result);
1836 DCHECK(!reading_);
1837 TransitionToState(STATE_CONNECTED_CALLBACK);
1838 return OnWriteResponseInfoToEntryComplete(result);
1839 }
1840
DoSendRequest()1841 int HttpCache::Transaction::DoSendRequest() {
1842 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoSendRequest",
1843 perfetto::Track(trace_id_));
1844 DCHECK(mode_ & WRITE || mode_ == NONE);
1845 DCHECK(!network_trans_.get());
1846
1847 send_request_since_ = TimeTicks::Now();
1848
1849 // Create a network transaction.
1850 int rv =
1851 cache_->network_layer_->CreateTransaction(priority_, &network_trans_);
1852
1853 if (rv != OK) {
1854 TransitionToState(STATE_FINISH_HEADERS);
1855 return rv;
1856 }
1857
1858 network_trans_->SetBeforeNetworkStartCallback(
1859 std::move(before_network_start_callback_));
1860 network_trans_->SetConnectedCallback(connected_callback_);
1861 network_trans_->SetRequestHeadersCallback(request_headers_callback_);
1862 network_trans_->SetEarlyResponseHeadersCallback(
1863 early_response_headers_callback_);
1864 network_trans_->SetResponseHeadersCallback(response_headers_callback_);
1865 if (is_shared_dictionary_read_allowed_callback_) {
1866 network_trans_->SetIsSharedDictionaryReadAllowedCallback(
1867 is_shared_dictionary_read_allowed_callback_);
1868 }
1869
1870 // Old load timing information, if any, is now obsolete.
1871 network_transaction_info_.old_network_trans_load_timing.reset();
1872 network_transaction_info_.old_remote_endpoint = IPEndPoint();
1873
1874 if (websocket_handshake_stream_base_create_helper_) {
1875 network_trans_->SetWebSocketHandshakeStreamCreateHelper(
1876 websocket_handshake_stream_base_create_helper_);
1877 }
1878
1879 TransitionToState(STATE_SEND_REQUEST_COMPLETE);
1880 rv = network_trans_->Start(request_, io_callback_, net_log_);
1881 if (rv != ERR_IO_PENDING && waiting_for_cache_io_) {
1882 // queue the state transition until the HttpCache transaction completes
1883 DCHECK(!pending_io_result_);
1884 pending_io_result_ = rv;
1885 rv = ERR_IO_PENDING;
1886 }
1887 return rv;
1888 }
1889
DoSendRequestComplete(int result)1890 int HttpCache::Transaction::DoSendRequestComplete(int result) {
1891 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoSendRequestComplete",
1892 perfetto::Track(trace_id_), "result", result, "elapsed",
1893 base::TimeTicks::Now() - send_request_since_);
1894 if (!cache_.get()) {
1895 TransitionToState(STATE_FINISH_HEADERS);
1896 return ERR_UNEXPECTED;
1897 }
1898
1899 // If we tried to conditionalize the request and failed, we know
1900 // we won't be reading from the cache after this point.
1901 if (couldnt_conditionalize_request_) {
1902 mode_ = WRITE;
1903 }
1904
1905 if (result == OK) {
1906 TransitionToState(STATE_SUCCESSFUL_SEND_REQUEST);
1907 return OK;
1908 }
1909
1910 const HttpResponseInfo* response = network_trans_->GetResponseInfo();
1911 response_.network_accessed = response->network_accessed;
1912 response_.was_fetched_via_proxy = response->was_fetched_via_proxy;
1913 response_.proxy_chain = response->proxy_chain;
1914 response_.restricted_prefetch = response->restricted_prefetch;
1915 response_.resolve_error_info = response->resolve_error_info;
1916
1917 // Do not record requests that have network errors or restarts.
1918 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
1919 if (IsCertificateError(result)) {
1920 // If we get a certificate error, then there is a certificate in ssl_info,
1921 // so GetResponseInfo() should never return NULL here.
1922 DCHECK(response);
1923 response_.ssl_info = response->ssl_info;
1924 } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
1925 DCHECK(response);
1926 response_.cert_request_info = response->cert_request_info;
1927 } else if (result == ERR_INCONSISTENT_IP_ADDRESS_SPACE) {
1928 DoomInconsistentEntry();
1929 } else if (response_.was_cached) {
1930 DoneWithEntry(/*entry_is_complete=*/true);
1931 }
1932
1933 TransitionToState(STATE_FINISH_HEADERS);
1934 return result;
1935 }
1936
1937 // We received the response headers and there is no error.
DoSuccessfulSendRequest()1938 int HttpCache::Transaction::DoSuccessfulSendRequest() {
1939 DCHECK(!new_response_);
1940 const HttpResponseInfo* new_response = network_trans_->GetResponseInfo();
1941 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoSuccessfulSendRequest",
1942 perfetto::Track(trace_id_), "response_code",
1943 new_response->headers->response_code());
1944
1945 if (new_response->headers->response_code() == net::HTTP_UNAUTHORIZED ||
1946 new_response->headers->response_code() ==
1947 net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
1948 SetAuthResponse(*new_response);
1949 if (!reading_) {
1950 TransitionToState(STATE_FINISH_HEADERS);
1951 return OK;
1952 }
1953
1954 // We initiated a second request the caller doesn't know about. We should be
1955 // able to authenticate this request because we should have authenticated
1956 // this URL moments ago.
1957 if (IsReadyToRestartForAuth()) {
1958 TransitionToState(STATE_SEND_REQUEST_COMPLETE);
1959 // In theory we should check to see if there are new cookies, but there
1960 // is no way to do that from here.
1961 return network_trans_->RestartWithAuth(AuthCredentials(), io_callback_);
1962 }
1963
1964 // We have to perform cleanup at this point so that at least the next
1965 // request can succeed. We do not retry at this point, because data
1966 // has been read and we have no way to gather credentials. We would
1967 // fail again, and potentially loop. This can happen if the credentials
1968 // expire while chrome is suspended.
1969 if (entry_) {
1970 DoomPartialEntry(false);
1971 }
1972 mode_ = NONE;
1973 partial_.reset();
1974 ResetNetworkTransaction();
1975 TransitionToState(STATE_FINISH_HEADERS);
1976 return ERR_CACHE_AUTH_FAILURE_AFTER_READ;
1977 }
1978
1979 new_response_ = new_response;
1980 if (!ValidatePartialResponse() && !auth_response_.headers.get()) {
1981 // Something went wrong with this request and we have to restart it.
1982 // If we have an authentication response, we are exposed to weird things
1983 // hapenning if the user cancels the authentication before we receive
1984 // the new response.
1985 net_log_.AddEvent(NetLogEventType::HTTP_CACHE_RE_SEND_PARTIAL_REQUEST);
1986 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
1987 SetResponse(HttpResponseInfo());
1988 ResetNetworkTransaction();
1989 new_response_ = nullptr;
1990 TransitionToState(STATE_SEND_REQUEST);
1991 return OK;
1992 }
1993
1994 if (handling_206_ && mode_ == READ_WRITE && !truncated_ && !is_sparse_) {
1995 // We have stored the full entry, but it changed and the server is
1996 // sending a range. We have to delete the old entry.
1997 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
1998 DoneWithEntry(false);
1999 }
2000
2001 if (mode_ == WRITE &&
2002 cache_entry_status_ != CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE) {
2003 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_NOT_IN_CACHE);
2004 }
2005
2006 // Invalidate any cached GET with a successful PUT, DELETE, or PATCH.
2007 if (mode_ == WRITE &&
2008 (method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH")) {
2009 if (NonErrorResponse(new_response_->headers->response_code()) &&
2010 (entry_ && !entry_->IsDoomed())) {
2011 int ret = cache_->DoomEntry(cache_key_, nullptr);
2012 DCHECK_EQ(OK, ret);
2013 }
2014 // Do not invalidate the entry if the request failed.
2015 DoneWithEntry(true);
2016 }
2017
2018 // Invalidate any cached GET with a successful POST. If the network isolation
2019 // key isn't populated with the split cache active, there will be nothing to
2020 // invalidate in the cache.
2021 if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) && method_ == "POST" &&
2022 NonErrorResponse(new_response_->headers->response_code()) &&
2023 (!HttpCache::IsSplitCacheEnabled() ||
2024 request_->network_isolation_key.IsFullyPopulated())) {
2025 cache_->DoomMainEntryForUrl(request_->url, request_->network_isolation_key,
2026 request_->is_subframe_document_resource);
2027 }
2028
2029 if (new_response_->headers->response_code() ==
2030 net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE &&
2031 (method_ == "GET" || method_ == "POST")) {
2032 // If there is an active entry it may be destroyed with this transaction.
2033 SetResponse(*new_response_);
2034 TransitionToState(STATE_FINISH_HEADERS);
2035 return OK;
2036 }
2037
2038 // Are we expecting a response to a conditional query?
2039 if (mode_ == READ_WRITE || mode_ == UPDATE) {
2040 if (new_response->headers->response_code() == net::HTTP_NOT_MODIFIED ||
2041 handling_206_) {
2042 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_VALIDATED);
2043 TransitionToState(STATE_UPDATE_CACHED_RESPONSE);
2044 return OK;
2045 }
2046 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_UPDATED);
2047 mode_ = WRITE;
2048 }
2049
2050 TransitionToState(STATE_OVERWRITE_CACHED_RESPONSE);
2051 return OK;
2052 }
2053
2054 // We received 304 or 206 and we want to update the cached response headers.
DoUpdateCachedResponse()2055 int HttpCache::Transaction::DoUpdateCachedResponse() {
2056 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoUpdateCachedResponse",
2057 perfetto::Track(trace_id_));
2058 int rv = OK;
2059 // Update the cached response based on the headers and properties of
2060 // new_response_.
2061 response_.headers->Update(*new_response_->headers.get());
2062 response_.stale_revalidate_timeout = base::Time();
2063 response_.response_time = new_response_->response_time;
2064 response_.request_time = new_response_->request_time;
2065 response_.network_accessed = new_response_->network_accessed;
2066 response_.unused_since_prefetch = new_response_->unused_since_prefetch;
2067 response_.restricted_prefetch = new_response_->restricted_prefetch;
2068 response_.ssl_info = new_response_->ssl_info;
2069 response_.dns_aliases = new_response_->dns_aliases;
2070
2071 // If the new response didn't have a vary header, we continue to use the
2072 // header from the stored response per the effect of headers->Update().
2073 // Update the data with the new/updated request headers.
2074 response_.vary_data.Init(*request_, *response_.headers);
2075
2076 if (ShouldDisableCaching(*response_.headers)) {
2077 if (!entry_->IsDoomed()) {
2078 int ret = cache_->DoomEntry(cache_key_, nullptr);
2079 DCHECK_EQ(OK, ret);
2080 }
2081 TransitionToState(STATE_UPDATE_CACHED_RESPONSE_COMPLETE);
2082 } else {
2083 // If we are already reading, we already updated the headers for this
2084 // request; doing it again will change Content-Length.
2085 if (!reading_) {
2086 TransitionToState(STATE_CACHE_WRITE_UPDATED_RESPONSE);
2087 rv = OK;
2088 } else {
2089 TransitionToState(STATE_UPDATE_CACHED_RESPONSE_COMPLETE);
2090 }
2091 }
2092
2093 return rv;
2094 }
2095
DoCacheWriteUpdatedResponse()2096 int HttpCache::Transaction::DoCacheWriteUpdatedResponse() {
2097 TRACE_EVENT_INSTANT("net",
2098 "HttpCacheTransaction::DoCacheWriteUpdatedResponse",
2099 perfetto::Track(trace_id_));
2100 TransitionToState(STATE_CACHE_WRITE_UPDATED_RESPONSE_COMPLETE);
2101 return WriteResponseInfoToEntry(response_, false);
2102 }
2103
DoCacheWriteUpdatedResponseComplete(int result)2104 int HttpCache::Transaction::DoCacheWriteUpdatedResponseComplete(int result) {
2105 TRACE_EVENT_INSTANT(
2106 "net", "HttpCacheTransaction::DoCacheWriteUpdatedResponseComplete",
2107 perfetto::Track(trace_id_), "result", result);
2108 TransitionToState(STATE_UPDATE_CACHED_RESPONSE_COMPLETE);
2109 return OnWriteResponseInfoToEntryComplete(result);
2110 }
2111
DoUpdateCachedResponseComplete(int result)2112 int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) {
2113 TRACE_EVENT_INSTANT("net",
2114 "HttpCacheTransaction::DoUpdateCachedResponseComplete",
2115 perfetto::Track(trace_id_), "result", result);
2116 if (mode_ == UPDATE) {
2117 DCHECK(!handling_206_);
2118 // We got a "not modified" response and already updated the corresponding
2119 // cache entry above.
2120 //
2121 // By stopping to write to the cache now, we make sure that the 304 rather
2122 // than the cached 200 response, is what will be returned to the user.
2123 UpdateSecurityHeadersBeforeForwarding();
2124 DoneWithEntry(true);
2125 } else if (entry_ && !handling_206_) {
2126 DCHECK_EQ(READ_WRITE, mode_);
2127 if ((!partial_ && !entry_->IsWritingInProgress()) ||
2128 (partial_ && partial_->IsLastRange())) {
2129 mode_ = READ;
2130 }
2131 // We no longer need the network transaction, so destroy it.
2132 if (network_trans_) {
2133 ResetNetworkTransaction();
2134 }
2135 } else if (entry_ && handling_206_ && truncated_ &&
2136 partial_->initial_validation()) {
2137 // We just finished the validation of a truncated entry, and the server
2138 // is willing to resume the operation. Now we go back and start serving
2139 // the first part to the user.
2140 if (network_trans_) {
2141 ResetNetworkTransaction();
2142 }
2143 new_response_ = nullptr;
2144 TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
2145 partial_->SetRangeToStartDownload();
2146 return OK;
2147 }
2148 TransitionToState(STATE_OVERWRITE_CACHED_RESPONSE);
2149 return OK;
2150 }
2151
DoOverwriteCachedResponse()2152 int HttpCache::Transaction::DoOverwriteCachedResponse() {
2153 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoOverwriteCachedResponse",
2154 perfetto::Track(trace_id_));
2155 if (mode_ & READ) {
2156 TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
2157 return OK;
2158 }
2159
2160 // We change the value of Content-Length for partial content.
2161 if (handling_206_ && partial_) {
2162 partial_->FixContentLength(new_response_->headers.get());
2163 }
2164
2165 SetResponse(*new_response_);
2166
2167 if (method_ == "HEAD") {
2168 // This response is replacing the cached one.
2169 DoneWithEntry(false);
2170 new_response_ = nullptr;
2171 TransitionToState(STATE_FINISH_HEADERS);
2172 return OK;
2173 }
2174
2175 if (handling_206_ && !CanResume(false)) {
2176 // There is no point in storing this resource because it will never be used.
2177 // This may change if we support LOAD_ONLY_FROM_CACHE with sparse entries.
2178 DoneWithEntry(false);
2179 if (partial_) {
2180 partial_->FixResponseHeaders(response_.headers.get(), true);
2181 }
2182 TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
2183 return OK;
2184 }
2185 // Mark the response with browser_run_id before it gets written.
2186 if (initial_request_->browser_run_id.has_value()) {
2187 response_.browser_run_id = initial_request_->browser_run_id;
2188 }
2189
2190 TransitionToState(STATE_CACHE_WRITE_RESPONSE);
2191 return OK;
2192 }
2193
DoCacheWriteResponse()2194 int HttpCache::Transaction::DoCacheWriteResponse() {
2195 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoCacheWriteResponse",
2196 perfetto::Track(trace_id_));
2197 DCHECK(response_.headers);
2198 // Invalidate any current entry with a successful response if this transaction
2199 // cannot write to this entry. This transaction then continues to read from
2200 // the network without writing to the backend.
2201 bool is_match = response_.headers->response_code() == net::HTTP_NOT_MODIFIED;
2202 if (entry_ && !entry_->CanTransactionWriteResponseHeaders(
2203 this, partial_ != nullptr, is_match)) {
2204 done_headers_create_new_entry_ = true;
2205
2206 // The transaction needs to overwrite this response. Doom the current entry,
2207 // create a new one (by going to STATE_INIT_ENTRY), and then jump straight
2208 // to writing out the response, bypassing the headers checks. The mode_ is
2209 // set to WRITE in order to doom any other existing entries that might exist
2210 // so that this transaction can go straight to writing a response.
2211 mode_ = WRITE;
2212 TransitionToState(STATE_INIT_ENTRY);
2213 cache_->DoomEntryValidationNoMatch(std::move(entry_));
2214 entry_.reset();
2215 return OK;
2216 }
2217
2218 TransitionToState(STATE_CACHE_WRITE_RESPONSE_COMPLETE);
2219 return WriteResponseInfoToEntry(response_, truncated_);
2220 }
2221
DoCacheWriteResponseComplete(int result)2222 int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) {
2223 TRACE_EVENT_INSTANT("net",
2224 "HttpCacheTransaction::DoCacheWriteResponseComplete",
2225 perfetto::Track(trace_id_), "result", result);
2226 TransitionToState(STATE_TRUNCATE_CACHED_DATA);
2227 return OnWriteResponseInfoToEntryComplete(result);
2228 }
2229
DoTruncateCachedData()2230 int HttpCache::Transaction::DoTruncateCachedData() {
2231 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoTruncateCachedData",
2232 perfetto::Track(trace_id_));
2233 TransitionToState(STATE_TRUNCATE_CACHED_DATA_COMPLETE);
2234 if (!entry_) {
2235 return OK;
2236 }
2237 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_WRITE_DATA);
2238 BeginDiskCacheAccessTimeCount();
2239 // Truncate the stream.
2240 return entry_->GetEntry()->WriteData(kResponseContentIndex, /*offset=*/0,
2241 /*buf=*/nullptr, /*buf_len=*/0,
2242 io_callback_, /*truncate=*/true);
2243 }
2244
DoTruncateCachedDataComplete(int result)2245 int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) {
2246 TRACE_EVENT_INSTANT("net",
2247 "HttpCacheTransaction::DoTruncateCachedDataComplete",
2248 perfetto::Track(trace_id_), "result", result);
2249 EndDiskCacheAccessTimeCount(DiskCacheAccessType::kWrite);
2250 if (entry_) {
2251 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_DATA,
2252 result);
2253 }
2254
2255 TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
2256 return OK;
2257 }
2258
DoPartialHeadersReceived()2259 int HttpCache::Transaction::DoPartialHeadersReceived() {
2260 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoPartialHeadersReceived",
2261 perfetto::Track(trace_id_));
2262 new_response_ = nullptr;
2263
2264 if (partial_ && mode_ != NONE && !reading_) {
2265 // We are about to return the headers for a byte-range request to the user,
2266 // so let's fix them.
2267 partial_->FixResponseHeaders(response_.headers.get(), true);
2268 }
2269 TransitionToState(STATE_FINISH_HEADERS);
2270 return OK;
2271 }
2272
DoHeadersPhaseCannotProceed(int result)2273 int HttpCache::Transaction::DoHeadersPhaseCannotProceed(int result) {
2274 // If its the Start state machine and it cannot proceed due to a cache
2275 // failure, restart this transaction.
2276 DCHECK(!reading_);
2277
2278 // Reset before invoking SetRequest() which can reset the request info sent to
2279 // network transaction.
2280 if (network_trans_) {
2281 network_trans_.reset();
2282 }
2283
2284 new_response_ = nullptr;
2285
2286 SetRequest(net_log_);
2287
2288 entry_.reset();
2289 new_entry_.reset();
2290 last_disk_cache_access_start_time_ = TimeTicks();
2291
2292 // TODO(https://crbug.com/1219402): This should probably clear `response_`,
2293 // too, once things are fixed so it's safe to do so.
2294
2295 // Bypass the cache for timeout scenario.
2296 if (result == ERR_CACHE_LOCK_TIMEOUT) {
2297 effective_load_flags_ |= LOAD_DISABLE_CACHE;
2298 }
2299
2300 TransitionToState(STATE_GET_BACKEND);
2301 return OK;
2302 }
2303
DoFinishHeaders(int result)2304 int HttpCache::Transaction::DoFinishHeaders(int result) {
2305 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoFinishHeaders",
2306 perfetto::Track(trace_id_), "result", result);
2307 if (!cache_.get() || !entry_ || result != OK) {
2308 TransitionToState(STATE_NONE);
2309 return result;
2310 }
2311
2312 TransitionToState(STATE_FINISH_HEADERS_COMPLETE);
2313
2314 // If it was an auth failure, this transaction should continue to be
2315 // headers_transaction till consumer takes an action, so no need to do
2316 // anything now.
2317 // TODO(crbug.com/740947). See the issue for a suggestion for cleaning the
2318 // state machine to be able to remove this condition.
2319 if (auth_response_.headers.get()) {
2320 return OK;
2321 }
2322
2323 // If the transaction needs to wait because another transaction is still
2324 // writing the response body, it will return ERR_IO_PENDING now and the
2325 // cache_io_callback_ will be invoked when the wait is done.
2326 int rv = cache_->DoneWithResponseHeaders(entry_, this, partial_ != nullptr);
2327 DCHECK(!reading_ || rv == OK) << "Expected OK, but got " << rv;
2328
2329 if (rv == ERR_IO_PENDING) {
2330 DCHECK(entry_lock_waiting_since_.is_null());
2331 entry_lock_waiting_since_ = TimeTicks::Now();
2332 AddCacheLockTimeoutHandler(entry_.get());
2333 }
2334 return rv;
2335 }
2336
DoFinishHeadersComplete(int rv)2337 int HttpCache::Transaction::DoFinishHeadersComplete(int rv) {
2338 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoFinishHeadersComplete",
2339 perfetto::Track(trace_id_), "result", rv);
2340 entry_lock_waiting_since_ = TimeTicks();
2341 if (rv == ERR_CACHE_RACE || rv == ERR_CACHE_LOCK_TIMEOUT) {
2342 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
2343 return rv;
2344 }
2345
2346 if (network_trans_ && InWriters()) {
2347 entry_->writers()->SetNetworkTransaction(this, std::move(network_trans_));
2348 moved_network_transaction_to_writers_ = true;
2349 }
2350
2351 // If already reading, that means it is a partial request coming back to the
2352 // headers phase, continue to the appropriate reading state.
2353 if (reading_) {
2354 int reading_state_rv = TransitionToReadingState();
2355 DCHECK_EQ(OK, reading_state_rv);
2356 return OK;
2357 }
2358
2359 TransitionToState(STATE_NONE);
2360 return rv;
2361 }
2362
DoNetworkReadCacheWrite()2363 int HttpCache::Transaction::DoNetworkReadCacheWrite() {
2364 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoNetworkReadCacheWrite",
2365 perfetto::Track(trace_id_), "read_offset", read_offset_,
2366 "read_buf_len", read_buf_len_);
2367 DCHECK(InWriters());
2368 TransitionToState(STATE_NETWORK_READ_CACHE_WRITE_COMPLETE);
2369 return entry_->writers()->Read(read_buf_, read_buf_len_, io_callback_, this);
2370 }
2371
DoNetworkReadCacheWriteComplete(int result)2372 int HttpCache::Transaction::DoNetworkReadCacheWriteComplete(int result) {
2373 TRACE_EVENT_INSTANT("net",
2374 "HttpCacheTransaction::DoNetworkReadCacheWriteComplete",
2375 perfetto::Track(trace_id_), "result", result);
2376 if (!cache_.get()) {
2377 TransitionToState(STATE_NONE);
2378 return ERR_UNEXPECTED;
2379 }
2380 // |result| will be error code in case of network read failure and |this|
2381 // cannot proceed further, so set entry_ to null. |result| will not be error
2382 // in case of cache write failure since |this| can continue to read from the
2383 // network. If response is completed, then also set entry to null.
2384 if (result < 0) {
2385 // We should have discovered this error in WriterAboutToBeRemovedFromEntry
2386 DCHECK_EQ(result, shared_writing_error_);
2387 DCHECK_EQ(NONE, mode_);
2388 DCHECK(!entry_);
2389 TransitionToState(STATE_NONE);
2390 return result;
2391 }
2392
2393 if (partial_) {
2394 return DoPartialNetworkReadCompleted(result);
2395 }
2396
2397 if (result == 0) {
2398 DCHECK_EQ(NONE, mode_);
2399 DCHECK(!entry_);
2400 } else {
2401 read_offset_ += result;
2402 }
2403 TransitionToState(STATE_NONE);
2404 return result;
2405 }
2406
DoPartialNetworkReadCompleted(int result)2407 int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) {
2408 DCHECK(partial_);
2409
2410 // Go to the next range if nothing returned or return the result.
2411 // TODO(shivanisha) Simplify this condition if possible. It was introduced
2412 // in https://codereview.chromium.org/545101
2413 if (result != 0 || truncated_ ||
2414 !(partial_->IsLastRange() || mode_ == WRITE)) {
2415 partial_->OnNetworkReadCompleted(result);
2416
2417 if (result == 0) {
2418 // We need to move on to the next range.
2419 if (network_trans_) {
2420 ResetNetworkTransaction();
2421 } else if (InWriters() && entry_->writers()->network_transaction()) {
2422 SaveNetworkTransactionInfo(*(entry_->writers()->network_transaction()));
2423 entry_->writers()->ResetNetworkTransaction();
2424 }
2425 TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
2426 } else {
2427 TransitionToState(STATE_NONE);
2428 }
2429 return result;
2430 }
2431
2432 // Request completed.
2433 if (result == 0) {
2434 DoneWithEntry(true);
2435 }
2436
2437 TransitionToState(STATE_NONE);
2438 return result;
2439 }
2440
DoNetworkRead()2441 int HttpCache::Transaction::DoNetworkRead() {
2442 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoNetworkRead",
2443 perfetto::Track(trace_id_), "read_offset", read_offset_,
2444 "read_buf_len", read_buf_len_);
2445 TransitionToState(STATE_NETWORK_READ_COMPLETE);
2446 return network_trans_->Read(read_buf_.get(), read_buf_len_, io_callback_);
2447 }
2448
DoNetworkReadComplete(int result)2449 int HttpCache::Transaction::DoNetworkReadComplete(int result) {
2450 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoNetworkReadComplete",
2451 perfetto::Track(trace_id_), "result", result);
2452
2453 if (!cache_.get()) {
2454 TransitionToState(STATE_NONE);
2455 return ERR_UNEXPECTED;
2456 }
2457
2458 if (partial_) {
2459 return DoPartialNetworkReadCompleted(result);
2460 }
2461
2462 TransitionToState(STATE_NONE);
2463 return result;
2464 }
2465
DoCacheReadData()2466 int HttpCache::Transaction::DoCacheReadData() {
2467 if (entry_) {
2468 DCHECK(InWriters() || entry_->TransactionInReaders(this));
2469 }
2470
2471 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoCacheReadData",
2472 perfetto::Track(trace_id_), "read_offset", read_offset_,
2473 "read_buf_len", read_buf_len_);
2474
2475 if (method_ == "HEAD") {
2476 TransitionToState(STATE_NONE);
2477 return 0;
2478 }
2479
2480 DCHECK(entry_);
2481 TransitionToState(STATE_CACHE_READ_DATA_COMPLETE);
2482
2483 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_READ_DATA);
2484 if (partial_) {
2485 return partial_->CacheRead(entry_->GetEntry(), read_buf_.get(),
2486 read_buf_len_, io_callback_);
2487 }
2488
2489 BeginDiskCacheAccessTimeCount();
2490 return entry_->GetEntry()->ReadData(kResponseContentIndex, read_offset_,
2491 read_buf_.get(), read_buf_len_,
2492 io_callback_);
2493 }
2494
DoCacheReadDataComplete(int result)2495 int HttpCache::Transaction::DoCacheReadDataComplete(int result) {
2496 EndDiskCacheAccessTimeCount(DiskCacheAccessType::kRead);
2497 if (entry_) {
2498 DCHECK(InWriters() || entry_->TransactionInReaders(this));
2499 }
2500
2501 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoCacheReadDataComplete",
2502 perfetto::Track(trace_id_), "result", result);
2503 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_READ_DATA,
2504 result);
2505
2506 if (!cache_.get()) {
2507 TransitionToState(STATE_NONE);
2508 return ERR_UNEXPECTED;
2509 }
2510
2511 if (partial_) {
2512 // Partial requests are confusing to report in histograms because they may
2513 // have multiple underlying requests.
2514 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
2515 return DoPartialCacheReadCompleted(result);
2516 }
2517
2518 if (result > 0) {
2519 read_offset_ += result;
2520 } else if (result == 0) { // End of file.
2521 DoneWithEntry(true);
2522 } else {
2523 return OnCacheReadError(result, false);
2524 }
2525
2526 TransitionToState(STATE_NONE);
2527 return result;
2528 }
2529
2530 //-----------------------------------------------------------------------------
2531
SetRequest(const NetLogWithSource & net_log)2532 void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log) {
2533 net_log_ = net_log;
2534
2535 // Reset the variables that might get set in this function. This is done
2536 // because this function can be invoked multiple times for a transaction.
2537 cache_entry_status_ = CacheEntryStatus::ENTRY_UNDEFINED;
2538 external_validation_.Reset();
2539 range_requested_ = false;
2540 partial_.reset();
2541
2542 request_ = initial_request_;
2543 custom_request_.reset();
2544
2545 effective_load_flags_ = request_->load_flags;
2546 method_ = request_->method;
2547
2548 if (cache_->mode() == DISABLE) {
2549 effective_load_flags_ |= LOAD_DISABLE_CACHE;
2550 }
2551
2552 // Some headers imply load flags. The order here is significant.
2553 //
2554 // LOAD_DISABLE_CACHE : no cache read or write
2555 // LOAD_BYPASS_CACHE : no cache read
2556 // LOAD_VALIDATE_CACHE : no cache read unless validation
2557 //
2558 // The former modes trump latter modes, so if we find a matching header we
2559 // can stop iterating kSpecialHeaders.
2560 //
2561 static const struct {
2562 // This field is not a raw_ptr<> because it was filtered by the rewriter
2563 // for: #global-scope
2564 RAW_PTR_EXCLUSION const HeaderNameAndValue* search;
2565 int load_flag;
2566 } kSpecialHeaders[] = {
2567 {kPassThroughHeaders, LOAD_DISABLE_CACHE},
2568 {kForceFetchHeaders, LOAD_BYPASS_CACHE},
2569 {kForceValidateHeaders, LOAD_VALIDATE_CACHE},
2570 };
2571
2572 bool range_found = false;
2573 bool external_validation_error = false;
2574 bool special_headers = false;
2575
2576 if (request_->extra_headers.HasHeader(HttpRequestHeaders::kRange)) {
2577 range_found = true;
2578 }
2579
2580 for (const auto& special_header : kSpecialHeaders) {
2581 if (HeaderMatches(request_->extra_headers, special_header.search)) {
2582 effective_load_flags_ |= special_header.load_flag;
2583 special_headers = true;
2584 break;
2585 }
2586 }
2587
2588 // Check for conditionalization headers which may correspond with a
2589 // cache validation request.
2590 for (size_t i = 0; i < std::size(kValidationHeaders); ++i) {
2591 const ValidationHeaderInfo& info = kValidationHeaders[i];
2592 std::string validation_value;
2593 if (request_->extra_headers.GetHeader(info.request_header_name,
2594 &validation_value)) {
2595 if (!external_validation_.values[i].empty() || validation_value.empty()) {
2596 external_validation_error = true;
2597 }
2598 external_validation_.values[i] = validation_value;
2599 external_validation_.initialized = true;
2600 }
2601 }
2602
2603 if (range_found || special_headers || external_validation_.initialized) {
2604 // Log the headers before request_ is modified.
2605 std::string empty;
2606 NetLogRequestHeaders(net_log_,
2607 NetLogEventType::HTTP_CACHE_CALLER_REQUEST_HEADERS,
2608 empty, &request_->extra_headers);
2609 }
2610
2611 // We don't support ranges and validation headers.
2612 if (range_found && external_validation_.initialized) {
2613 LOG(WARNING) << "Byte ranges AND validation headers found.";
2614 effective_load_flags_ |= LOAD_DISABLE_CACHE;
2615 }
2616
2617 // If there is more than one validation header, we can't treat this request as
2618 // a cache validation, since we don't know for sure which header the server
2619 // will give us a response for (and they could be contradictory).
2620 if (external_validation_error) {
2621 LOG(WARNING) << "Multiple or malformed validation headers found.";
2622 effective_load_flags_ |= LOAD_DISABLE_CACHE;
2623 }
2624
2625 if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) {
2626 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
2627 partial_ = std::make_unique<PartialData>();
2628 if (method_ == "GET" && partial_->Init(request_->extra_headers)) {
2629 // We will be modifying the actual range requested to the server, so
2630 // let's remove the header here.
2631 // Note that custom_request_ is a shallow copy so will keep the same
2632 // pointer to upload data stream as in the original request.
2633 custom_request_ = std::make_unique<HttpRequestInfo>(*request_);
2634 custom_request_->extra_headers.RemoveHeader(HttpRequestHeaders::kRange);
2635 request_ = custom_request_.get();
2636 partial_->SetHeaders(custom_request_->extra_headers);
2637 } else {
2638 // The range is invalid or we cannot handle it properly.
2639 VLOG(1) << "Invalid byte range found.";
2640 effective_load_flags_ |= LOAD_DISABLE_CACHE;
2641 partial_.reset(nullptr);
2642 }
2643 }
2644 }
2645
ShouldPassThrough()2646 bool HttpCache::Transaction::ShouldPassThrough() {
2647 bool cacheable = true;
2648
2649 // We may have a null disk_cache if there is an error we cannot recover from,
2650 // like not enough disk space, or sharing violations.
2651 if (!cache_->disk_cache_.get()) {
2652 cacheable = false;
2653 } else if (effective_load_flags_ & LOAD_DISABLE_CACHE) {
2654 cacheable = false;
2655 }
2656 // Prevent resources whose origin is opaque from being cached. Blink's memory
2657 // cache should take care of reusing resources within the current page load,
2658 // but otherwise a resource with an opaque top-frame origin won’t be used
2659 // again. Also, if the request does not have a top frame origin, bypass the
2660 // cache otherwise resources from different pages could share a cached entry
2661 // in such cases.
2662 else if (HttpCache::IsSplitCacheEnabled() &&
2663 request_->network_isolation_key.IsTransient()) {
2664 cacheable = false;
2665 } else if (method_ == "GET" || method_ == "HEAD") {
2666 } else if (method_ == "POST" && request_->upload_data_stream &&
2667 request_->upload_data_stream->identifier()) {
2668 } else if (method_ == "PUT" && request_->upload_data_stream) {
2669 }
2670 // DELETE and PATCH requests may result in invalidating the cache, so cannot
2671 // just pass through.
2672 else if (method_ == "DELETE" || method_ == "PATCH") {
2673 } else {
2674 cacheable = false;
2675 }
2676
2677 return !cacheable;
2678 }
2679
BeginCacheRead()2680 int HttpCache::Transaction::BeginCacheRead() {
2681 // We don't support any combination of LOAD_ONLY_FROM_CACHE and byte ranges.
2682 // It's possible to trigger this from JavaScript using the Fetch API with
2683 // `cache: 'only-if-cached'` so ideally we should support it.
2684 // TODO(ricea): Correctly read from the cache in this case.
2685 if (response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT ||
2686 partial_) {
2687 TransitionToState(STATE_FINISH_HEADERS);
2688 return ERR_CACHE_MISS;
2689 }
2690
2691 // We don't have the whole resource.
2692 if (truncated_) {
2693 TransitionToState(STATE_FINISH_HEADERS);
2694 return ERR_CACHE_MISS;
2695 }
2696
2697 if (RequiresValidation() != VALIDATION_NONE) {
2698 TransitionToState(STATE_FINISH_HEADERS);
2699 return ERR_CACHE_MISS;
2700 }
2701
2702 if (method_ == "HEAD") {
2703 FixHeadersForHead();
2704 }
2705
2706 TransitionToState(STATE_FINISH_HEADERS);
2707 return OK;
2708 }
2709
BeginCacheValidation()2710 int HttpCache::Transaction::BeginCacheValidation() {
2711 DCHECK_EQ(mode_, READ_WRITE);
2712
2713 ValidationType required_validation = RequiresValidation();
2714
2715 bool skip_validation = (required_validation == VALIDATION_NONE);
2716 bool needs_stale_while_revalidate_cache_update = false;
2717
2718 if ((effective_load_flags_ & LOAD_SUPPORT_ASYNC_REVALIDATION) &&
2719 required_validation == VALIDATION_ASYNCHRONOUS) {
2720 DCHECK_EQ(request_->method, "GET");
2721 skip_validation = true;
2722 response_.async_revalidation_requested = true;
2723 needs_stale_while_revalidate_cache_update =
2724 response_.stale_revalidate_timeout.is_null();
2725 }
2726
2727 if (method_ == "HEAD" && (truncated_ || response_.headers->response_code() ==
2728 net::HTTP_PARTIAL_CONTENT)) {
2729 DCHECK(!partial_);
2730 if (skip_validation) {
2731 DCHECK(!reading_);
2732 TransitionToState(STATE_CONNECTED_CALLBACK);
2733 return OK;
2734 }
2735
2736 // Bail out!
2737 TransitionToState(STATE_SEND_REQUEST);
2738 mode_ = NONE;
2739 return OK;
2740 }
2741
2742 if (truncated_) {
2743 // Truncated entries can cause partial gets, so we shouldn't record this
2744 // load in histograms.
2745 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
2746 skip_validation = !partial_->initial_validation();
2747 }
2748
2749 // If this is the first request (!reading_) of a 206 entry (is_sparse_) that
2750 // doesn't actually cover the entire file (which with !reading would require
2751 // partial->IsLastRange()), and the user is requesting the whole thing
2752 // (!partial_->range_requested()), make sure to validate the first chunk,
2753 // since afterwards it will be too late if it's actually out-of-date (or the
2754 // server bungles invalidation). This is limited to the whole-file request
2755 // as a targeted fix for https://crbug.com/888742 while avoiding extra
2756 // requests in other cases, but the problem can occur more generally as well;
2757 // it's just a lot less likely with applications actively using ranges.
2758 // See https://crbug.com/902724 for the more general case.
2759 bool first_read_of_full_from_partial =
2760 is_sparse_ && !reading_ &&
2761 (partial_ && !partial_->range_requested() && !partial_->IsLastRange());
2762
2763 if (partial_ && (is_sparse_ || truncated_) &&
2764 (!partial_->IsCurrentRangeCached() || invalid_range_ ||
2765 first_read_of_full_from_partial)) {
2766 // Force revalidation for sparse or truncated entries. Note that we don't
2767 // want to ignore the regular validation logic just because a byte range was
2768 // part of the request.
2769 skip_validation = false;
2770 }
2771
2772 if (skip_validation) {
2773 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED);
2774 DCHECK(!reading_);
2775 TransitionToState(needs_stale_while_revalidate_cache_update
2776 ? STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT
2777 : STATE_CONNECTED_CALLBACK);
2778 return OK;
2779 } else {
2780 // Make the network request conditional, to see if we may reuse our cached
2781 // response. If we cannot do so, then we just resort to a normal fetch.
2782 // Our mode remains READ_WRITE for a conditional request. Even if the
2783 // conditionalization fails, we don't switch to WRITE mode until we
2784 // know we won't be falling back to using the cache entry in the
2785 // LOAD_FROM_CACHE_IF_OFFLINE case.
2786 if (!ConditionalizeRequest()) {
2787 couldnt_conditionalize_request_ = true;
2788 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE);
2789 if (partial_) {
2790 return DoRestartPartialRequest();
2791 }
2792
2793 DCHECK_NE(net::HTTP_PARTIAL_CONTENT, response_.headers->response_code());
2794 }
2795 TransitionToState(STATE_SEND_REQUEST);
2796 }
2797 return OK;
2798 }
2799
BeginPartialCacheValidation()2800 int HttpCache::Transaction::BeginPartialCacheValidation() {
2801 DCHECK_EQ(mode_, READ_WRITE);
2802
2803 if (response_.headers->response_code() != net::HTTP_PARTIAL_CONTENT &&
2804 !partial_ && !truncated_) {
2805 return BeginCacheValidation();
2806 }
2807
2808 // Partial requests should not be recorded in histograms.
2809 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
2810 if (method_ == "HEAD") {
2811 return BeginCacheValidation();
2812 }
2813
2814 if (!range_requested_) {
2815 // The request is not for a range, but we have stored just ranges.
2816
2817 partial_ = std::make_unique<PartialData>();
2818 partial_->SetHeaders(request_->extra_headers);
2819 if (!custom_request_.get()) {
2820 custom_request_ = std::make_unique<HttpRequestInfo>(*request_);
2821 request_ = custom_request_.get();
2822 }
2823 }
2824
2825 TransitionToState(STATE_CACHE_QUERY_DATA);
2826 return OK;
2827 }
2828
2829 // This should only be called once per request.
ValidateEntryHeadersAndContinue()2830 int HttpCache::Transaction::ValidateEntryHeadersAndContinue() {
2831 DCHECK_EQ(mode_, READ_WRITE);
2832
2833 if (!partial_->UpdateFromStoredHeaders(response_.headers.get(),
2834 entry_->GetEntry(), truncated_,
2835 entry_->IsWritingInProgress())) {
2836 return DoRestartPartialRequest();
2837 }
2838
2839 if (response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT) {
2840 is_sparse_ = true;
2841 }
2842
2843 if (!partial_->IsRequestedRangeOK()) {
2844 // The stored data is fine, but the request may be invalid.
2845 invalid_range_ = true;
2846 }
2847
2848 TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
2849 return OK;
2850 }
2851
2852 bool HttpCache::Transaction::
ExternallyConditionalizedValidationHeadersMatchEntry() const2853 ExternallyConditionalizedValidationHeadersMatchEntry() const {
2854 DCHECK(external_validation_.initialized);
2855
2856 for (size_t i = 0; i < std::size(kValidationHeaders); i++) {
2857 if (external_validation_.values[i].empty()) {
2858 continue;
2859 }
2860
2861 // Retrieve either the cached response's "etag" or "last-modified" header.
2862 std::string validator;
2863 response_.headers->EnumerateHeader(
2864 nullptr, kValidationHeaders[i].related_response_header_name,
2865 &validator);
2866
2867 if (validator != external_validation_.values[i]) {
2868 return false;
2869 }
2870 }
2871
2872 return true;
2873 }
2874
BeginExternallyConditionalizedRequest()2875 int HttpCache::Transaction::BeginExternallyConditionalizedRequest() {
2876 DCHECK_EQ(UPDATE, mode_);
2877
2878 if (response_.headers->response_code() != net::HTTP_OK || truncated_ ||
2879 !ExternallyConditionalizedValidationHeadersMatchEntry()) {
2880 // The externally conditionalized request is not a validation request
2881 // for our existing cache entry. Proceed with caching disabled.
2882 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
2883 DoneWithEntry(true);
2884 }
2885
2886 TransitionToState(STATE_SEND_REQUEST);
2887 return OK;
2888 }
2889
RestartNetworkRequest()2890 int HttpCache::Transaction::RestartNetworkRequest() {
2891 DCHECK(mode_ & WRITE || mode_ == NONE);
2892 DCHECK(network_trans_.get());
2893 DCHECK_EQ(STATE_NONE, next_state_);
2894
2895 next_state_ = STATE_SEND_REQUEST_COMPLETE;
2896 int rv = network_trans_->RestartIgnoringLastError(io_callback_);
2897 if (rv != ERR_IO_PENDING) {
2898 return DoLoop(rv);
2899 }
2900 return rv;
2901 }
2902
RestartNetworkRequestWithCertificate(scoped_refptr<X509Certificate> client_cert,scoped_refptr<SSLPrivateKey> client_private_key)2903 int HttpCache::Transaction::RestartNetworkRequestWithCertificate(
2904 scoped_refptr<X509Certificate> client_cert,
2905 scoped_refptr<SSLPrivateKey> client_private_key) {
2906 DCHECK(mode_ & WRITE || mode_ == NONE);
2907 DCHECK(network_trans_.get());
2908 DCHECK_EQ(STATE_NONE, next_state_);
2909
2910 next_state_ = STATE_SEND_REQUEST_COMPLETE;
2911 int rv = network_trans_->RestartWithCertificate(
2912 std::move(client_cert), std::move(client_private_key), io_callback_);
2913 if (rv != ERR_IO_PENDING) {
2914 return DoLoop(rv);
2915 }
2916 return rv;
2917 }
2918
RestartNetworkRequestWithAuth(const AuthCredentials & credentials)2919 int HttpCache::Transaction::RestartNetworkRequestWithAuth(
2920 const AuthCredentials& credentials) {
2921 DCHECK(mode_ & WRITE || mode_ == NONE);
2922 DCHECK(network_trans_.get());
2923 DCHECK_EQ(STATE_NONE, next_state_);
2924
2925 next_state_ = STATE_SEND_REQUEST_COMPLETE;
2926 int rv = network_trans_->RestartWithAuth(credentials, io_callback_);
2927 if (rv != ERR_IO_PENDING) {
2928 return DoLoop(rv);
2929 }
2930 return rv;
2931 }
2932
RequiresValidation()2933 ValidationType HttpCache::Transaction::RequiresValidation() {
2934 // TODO(darin): need to do more work here:
2935 // - make sure we have a matching request method
2936 // - watch out for cached responses that depend on authentication
2937
2938 if (!(effective_load_flags_ & LOAD_SKIP_VARY_CHECK) &&
2939 response_.vary_data.is_valid() &&
2940 !response_.vary_data.MatchesRequest(*request_,
2941 *response_.headers.get())) {
2942 vary_mismatch_ = true;
2943 validation_cause_ = VALIDATION_CAUSE_VARY_MISMATCH;
2944 return VALIDATION_SYNCHRONOUS;
2945 }
2946
2947 if (effective_load_flags_ & LOAD_SKIP_CACHE_VALIDATION) {
2948 return VALIDATION_NONE;
2949 }
2950
2951 if (method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH") {
2952 return VALIDATION_SYNCHRONOUS;
2953 }
2954
2955 bool validate_flag = effective_load_flags_ & LOAD_VALIDATE_CACHE;
2956
2957 ValidationType validation_required_by_headers =
2958 validate_flag ? VALIDATION_SYNCHRONOUS
2959 : response_.headers->RequiresValidation(
2960 response_.request_time, response_.response_time,
2961 cache_->clock_->Now());
2962
2963 base::TimeDelta response_time_in_cache =
2964 cache_->clock_->Now() - response_.response_time;
2965
2966 if (!base::FeatureList::IsEnabled(
2967 features::kPrefetchFollowsNormalCacheSemantics) &&
2968 !(effective_load_flags_ & LOAD_PREFETCH) &&
2969 (response_time_in_cache >= base::TimeDelta())) {
2970 bool reused_within_time_window =
2971 response_time_in_cache < base::Minutes(kPrefetchReuseMins);
2972 bool first_reuse = response_.unused_since_prefetch;
2973
2974 // The first use of a resource after prefetch within a short window skips
2975 // validation.
2976 if (first_reuse && reused_within_time_window) {
2977 return VALIDATION_NONE;
2978 }
2979 }
2980
2981 if (validate_flag) {
2982 validation_cause_ = VALIDATION_CAUSE_VALIDATE_FLAG;
2983 return VALIDATION_SYNCHRONOUS;
2984 }
2985
2986 if (validation_required_by_headers != VALIDATION_NONE) {
2987 HttpResponseHeaders::FreshnessLifetimes lifetimes =
2988 response_.headers->GetFreshnessLifetimes(response_.response_time);
2989 if (lifetimes.freshness == base::TimeDelta()) {
2990 validation_cause_ = VALIDATION_CAUSE_ZERO_FRESHNESS;
2991 } else {
2992 validation_cause_ = VALIDATION_CAUSE_STALE;
2993 }
2994 }
2995
2996 if (validation_required_by_headers == VALIDATION_ASYNCHRONOUS) {
2997 // Asynchronous revalidation is only supported for GET methods.
2998 if (request_->method != "GET") {
2999 return VALIDATION_SYNCHRONOUS;
3000 }
3001
3002 // If the timeout on the staleness revalidation is set don't hand out
3003 // a resource that hasn't been async validated.
3004 if (!response_.stale_revalidate_timeout.is_null() &&
3005 response_.stale_revalidate_timeout < cache_->clock_->Now()) {
3006 return VALIDATION_SYNCHRONOUS;
3007 }
3008 }
3009
3010 return validation_required_by_headers;
3011 }
3012
IsResponseConditionalizable(std::string * etag_value,std::string * last_modified_value) const3013 bool HttpCache::Transaction::IsResponseConditionalizable(
3014 std::string* etag_value,
3015 std::string* last_modified_value) const {
3016 DCHECK(response_.headers.get());
3017
3018 // This only makes sense for cached 200 or 206 responses.
3019 if (response_.headers->response_code() != net::HTTP_OK &&
3020 response_.headers->response_code() != net::HTTP_PARTIAL_CONTENT) {
3021 return false;
3022 }
3023
3024 // Just use the first available ETag and/or Last-Modified header value.
3025 // TODO(darin): Or should we use the last?
3026
3027 if (response_.headers->GetHttpVersion() >= HttpVersion(1, 1)) {
3028 response_.headers->EnumerateHeader(nullptr, "etag", etag_value);
3029 }
3030
3031 response_.headers->EnumerateHeader(nullptr, "last-modified",
3032 last_modified_value);
3033
3034 if (etag_value->empty() && last_modified_value->empty()) {
3035 return false;
3036 }
3037
3038 return true;
3039 }
3040
ShouldOpenOnlyMethods() const3041 bool HttpCache::Transaction::ShouldOpenOnlyMethods() const {
3042 // These methods indicate that we should only try to open an entry and not
3043 // fallback to create.
3044 return method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH" ||
3045 (method_ == "HEAD" && mode_ == READ_WRITE);
3046 }
3047
ConditionalizeRequest()3048 bool HttpCache::Transaction::ConditionalizeRequest() {
3049 DCHECK(response_.headers.get());
3050
3051 if (method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH") {
3052 return false;
3053 }
3054
3055 if (fail_conditionalization_for_test_) {
3056 return false;
3057 }
3058
3059 std::string etag_value;
3060 std::string last_modified_value;
3061 if (!IsResponseConditionalizable(&etag_value, &last_modified_value)) {
3062 return false;
3063 }
3064
3065 DCHECK(response_.headers->response_code() != net::HTTP_PARTIAL_CONTENT ||
3066 response_.headers->HasStrongValidators());
3067
3068 if (vary_mismatch_) {
3069 // Can't rely on last-modified if vary is different.
3070 last_modified_value.clear();
3071 if (etag_value.empty()) {
3072 return false;
3073 }
3074 }
3075
3076 if (!partial_) {
3077 // Need to customize the request, so this forces us to allocate :(
3078 custom_request_ = std::make_unique<HttpRequestInfo>(*request_);
3079 request_ = custom_request_.get();
3080 }
3081 DCHECK(custom_request_.get());
3082
3083 bool use_if_range =
3084 partial_ && !partial_->IsCurrentRangeCached() && !invalid_range_;
3085
3086 if (!etag_value.empty()) {
3087 if (use_if_range) {
3088 // We don't want to switch to WRITE mode if we don't have this block of a
3089 // byte-range request because we may have other parts cached.
3090 custom_request_->extra_headers.SetHeader(HttpRequestHeaders::kIfRange,
3091 etag_value);
3092 } else {
3093 custom_request_->extra_headers.SetHeader(HttpRequestHeaders::kIfNoneMatch,
3094 etag_value);
3095 }
3096 // For byte-range requests, make sure that we use only one way to validate
3097 // the request.
3098 if (partial_ && !partial_->IsCurrentRangeCached()) {
3099 return true;
3100 }
3101 }
3102
3103 if (!last_modified_value.empty()) {
3104 if (use_if_range) {
3105 custom_request_->extra_headers.SetHeader(HttpRequestHeaders::kIfRange,
3106 last_modified_value);
3107 } else {
3108 custom_request_->extra_headers.SetHeader(
3109 HttpRequestHeaders::kIfModifiedSince, last_modified_value);
3110 }
3111 }
3112
3113 return true;
3114 }
3115
MaybeRejectBasedOnEntryInMemoryData(uint8_t in_memory_info)3116 bool HttpCache::Transaction::MaybeRejectBasedOnEntryInMemoryData(
3117 uint8_t in_memory_info) {
3118 // Not going to be clever with those...
3119 if (partial_) {
3120 return false;
3121 }
3122
3123 // Avoiding open based on in-memory hints requires us to be permitted to
3124 // modify the cache, including deleting an old entry. Only the READ_WRITE
3125 // and WRITE modes permit that... and WRITE never tries to open entries in the
3126 // first place, so we shouldn't see it here.
3127 DCHECK_NE(mode_, WRITE);
3128 if (mode_ != READ_WRITE) {
3129 return false;
3130 }
3131
3132 // If we are loading ignoring cache validity (aka back button), obviously
3133 // can't reject things based on it. Also if LOAD_ONLY_FROM_CACHE there is no
3134 // hope of network offering anything better.
3135 if (effective_load_flags_ & LOAD_SKIP_CACHE_VALIDATION ||
3136 effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
3137 return false;
3138 }
3139
3140 return (in_memory_info & HINT_UNUSABLE_PER_CACHING_HEADERS) ==
3141 HINT_UNUSABLE_PER_CACHING_HEADERS;
3142 }
3143
ComputeUnusablePerCachingHeaders()3144 bool HttpCache::Transaction::ComputeUnusablePerCachingHeaders() {
3145 // unused_since_prefetch overrides some caching headers, so it may be useful
3146 // regardless of what they say.
3147 if (response_.unused_since_prefetch) {
3148 return false;
3149 }
3150
3151 // Has an e-tag or last-modified: we can probably send a conditional request,
3152 // so it's potentially useful.
3153 std::string etag_ignored, last_modified_ignored;
3154 if (IsResponseConditionalizable(&etag_ignored, &last_modified_ignored)) {
3155 return false;
3156 }
3157
3158 // If none of the above is true and the entry has zero freshness, then it
3159 // won't be usable absent load flag override.
3160 return response_.headers->GetFreshnessLifetimes(response_.response_time)
3161 .freshness.is_zero();
3162 }
3163
3164 // We just received some headers from the server. We may have asked for a range,
3165 // in which case partial_ has an object. This could be the first network request
3166 // we make to fulfill the original request, or we may be already reading (from
3167 // the net and / or the cache). If we are not expecting a certain response, we
3168 // just bypass the cache for this request (but again, maybe we are reading), and
3169 // delete partial_ (so we are not able to "fix" the headers that we return to
3170 // the user). This results in either a weird response for the caller (we don't
3171 // expect it after all), or maybe a range that was not exactly what it was asked
3172 // for.
3173 //
3174 // If the server is simply telling us that the resource has changed, we delete
3175 // the cached entry and restart the request as the caller intended (by returning
3176 // false from this method). However, we may not be able to do that at any point,
3177 // for instance if we already returned the headers to the user.
3178 //
3179 // WARNING: Whenever this code returns false, it has to make sure that the next
3180 // time it is called it will return true so that we don't keep retrying the
3181 // request.
ValidatePartialResponse()3182 bool HttpCache::Transaction::ValidatePartialResponse() {
3183 const HttpResponseHeaders* headers = new_response_->headers.get();
3184 int response_code = headers->response_code();
3185 bool partial_response = (response_code == net::HTTP_PARTIAL_CONTENT);
3186 handling_206_ = false;
3187
3188 if (!entry_ || method_ != "GET") {
3189 return true;
3190 }
3191
3192 if (invalid_range_) {
3193 // We gave up trying to match this request with the stored data. If the
3194 // server is ok with the request, delete the entry, otherwise just ignore
3195 // this request
3196 DCHECK(!reading_);
3197 if (partial_response || response_code == net::HTTP_OK) {
3198 DoomPartialEntry(true);
3199 mode_ = NONE;
3200 } else {
3201 if (response_code == net::HTTP_NOT_MODIFIED) {
3202 // Change the response code of the request to be 416 (Requested range
3203 // not satisfiable).
3204 SetResponse(*new_response_);
3205 partial_->FixResponseHeaders(response_.headers.get(), false);
3206 }
3207 IgnoreRangeRequest();
3208 }
3209 return true;
3210 }
3211
3212 if (!partial_) {
3213 // We are not expecting 206 but we may have one.
3214 if (partial_response) {
3215 IgnoreRangeRequest();
3216 }
3217
3218 return true;
3219 }
3220
3221 // TODO(rvargas): Do we need to consider other results here?.
3222 bool failure = response_code == net::HTTP_OK ||
3223 response_code == net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE;
3224
3225 if (partial_->IsCurrentRangeCached()) {
3226 // We asked for "If-None-Match: " so a 206 means a new object.
3227 if (partial_response) {
3228 failure = true;
3229 }
3230
3231 if (response_code == net::HTTP_NOT_MODIFIED &&
3232 partial_->ResponseHeadersOK(headers)) {
3233 return true;
3234 }
3235 } else {
3236 // We asked for "If-Range: " so a 206 means just another range.
3237 if (partial_response) {
3238 if (partial_->ResponseHeadersOK(headers)) {
3239 handling_206_ = true;
3240 return true;
3241 } else {
3242 failure = true;
3243 }
3244 }
3245
3246 if (!reading_ && !is_sparse_ && !partial_response) {
3247 // See if we can ignore the fact that we issued a byte range request.
3248 // If the server sends 200, just store it. If it sends an error, redirect
3249 // or something else, we may store the response as long as we didn't have
3250 // anything already stored.
3251 if (response_code == net::HTTP_OK ||
3252 (!truncated_ && response_code != net::HTTP_NOT_MODIFIED &&
3253 response_code != net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE)) {
3254 // The server is sending something else, and we can save it.
3255 DCHECK((truncated_ && !partial_->IsLastRange()) || range_requested_);
3256 partial_.reset();
3257 truncated_ = false;
3258 return true;
3259 }
3260 }
3261
3262 // 304 is not expected here, but we'll spare the entry (unless it was
3263 // truncated).
3264 if (truncated_) {
3265 failure = true;
3266 }
3267 }
3268
3269 if (failure) {
3270 // We cannot truncate this entry, it has to be deleted.
3271 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
3272 mode_ = NONE;
3273 if (is_sparse_ || truncated_) {
3274 // There was something cached to start with, either sparsed data (206), or
3275 // a truncated 200, which means that we probably modified the request,
3276 // adding a byte range or modifying the range requested by the caller.
3277 if (!reading_ && !partial_->IsLastRange()) {
3278 // We have not returned anything to the caller yet so it should be safe
3279 // to issue another network request, this time without us messing up the
3280 // headers.
3281 ResetPartialState(true);
3282 return false;
3283 }
3284 LOG(WARNING) << "Failed to revalidate partial entry";
3285 }
3286 DoomPartialEntry(true);
3287 return true;
3288 }
3289
3290 IgnoreRangeRequest();
3291 return true;
3292 }
3293
IgnoreRangeRequest()3294 void HttpCache::Transaction::IgnoreRangeRequest() {
3295 // We have a problem. We may or may not be reading already (in which case we
3296 // returned the headers), but we'll just pretend that this request is not
3297 // using the cache and see what happens. Most likely this is the first
3298 // response from the server (it's not changing its mind midway, right?).
3299 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
3300 DoneWithEntry(mode_ != WRITE);
3301 partial_.reset(nullptr);
3302 }
3303
3304 // Called to signal to the consumer that we are about to read headers from a
3305 // cached entry originally read from a given IP endpoint.
DoConnectedCallback()3306 int HttpCache::Transaction::DoConnectedCallback() {
3307 TransitionToState(STATE_CONNECTED_CALLBACK_COMPLETE);
3308 if (connected_callback_.is_null()) {
3309 return OK;
3310 }
3311
3312 auto type = response_.was_fetched_via_proxy ? TransportType::kCachedFromProxy
3313 : TransportType::kCached;
3314 return connected_callback_.Run(
3315 TransportInfo(type, response_.remote_endpoint, /*accept_ch_frame_arg=*/"",
3316 /*cert_is_issued_by_known_root=*/false, kProtoUnknown),
3317 io_callback_);
3318 }
3319
DoConnectedCallbackComplete(int result)3320 int HttpCache::Transaction::DoConnectedCallbackComplete(int result) {
3321 if (result != OK) {
3322 if (result ==
3323 ERR_CACHED_IP_ADDRESS_SPACE_BLOCKED_BY_PRIVATE_NETWORK_ACCESS_POLICY) {
3324 DoomInconsistentEntry();
3325 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
3326 TransitionToState(reading_ ? STATE_SEND_REQUEST
3327 : STATE_HEADERS_PHASE_CANNOT_PROCEED);
3328 return OK;
3329 }
3330
3331 if (result == ERR_INCONSISTENT_IP_ADDRESS_SPACE) {
3332 DoomInconsistentEntry();
3333 } else {
3334 // Release the entry for further use - we are done using it.
3335 DoneWithEntry(/*entry_is_complete=*/true);
3336 }
3337
3338 TransitionToState(STATE_NONE);
3339 return result;
3340 }
3341
3342 if (reading_) {
3343 // We can only get here if we're reading a partial range of bytes from the
3344 // cache. In that case, proceed to read the bytes themselves.
3345 DCHECK(partial_);
3346 TransitionToState(STATE_CACHE_READ_DATA);
3347 } else {
3348 // Otherwise, we have just read headers from the cache.
3349 TransitionToState(STATE_SETUP_ENTRY_FOR_READ);
3350 }
3351 return OK;
3352 }
3353
DoomInconsistentEntry()3354 void HttpCache::Transaction::DoomInconsistentEntry() {
3355 // Explicitly call `DoomActiveEntry()` ourselves before calling
3356 // `DoneWithEntry()` because we cannot rely on the latter doing it for us.
3357 // Indeed, `DoneWithEntry(false)` does not call `DoomActiveEntry()` if either
3358 // of the following conditions hold:
3359 //
3360 // - the transaction uses the cache in read-only mode
3361 // - the transaction has passed the headers phase and is reading
3362 //
3363 // Inconsistent cache entries can cause deterministic failures even in
3364 // read-only mode, so they should be doomed anyway. They can also be detected
3365 // during the reading phase in the case of split range requests, since those
3366 // requests can result in multiple connections being obtained to different
3367 // remote endpoints.
3368 cache_->DoomActiveEntry(cache_key_);
3369 DoneWithEntry(/*entry_is_complete=*/false);
3370 }
3371
FixHeadersForHead()3372 void HttpCache::Transaction::FixHeadersForHead() {
3373 if (response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT) {
3374 response_.headers->RemoveHeader("Content-Range");
3375 response_.headers->ReplaceStatusLine("HTTP/1.1 200 OK");
3376 }
3377 }
3378
DoSetupEntryForRead()3379 int HttpCache::Transaction::DoSetupEntryForRead() {
3380 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoSetupEntryForRead",
3381 perfetto::Track(trace_id_));
3382 if (network_trans_) {
3383 ResetNetworkTransaction();
3384 }
3385
3386 if (!entry_) {
3387 // Entry got destroyed when twiddling SWR bits.
3388 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
3389 return OK;
3390 }
3391
3392 if (partial_) {
3393 if (truncated_ || is_sparse_ ||
3394 (!invalid_range_ &&
3395 (response_.headers->response_code() == net::HTTP_OK ||
3396 response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT))) {
3397 // We are going to return the saved response headers to the caller, so
3398 // we may need to adjust them first. In cases we are handling a range
3399 // request to a regular entry, we want the response to be a 200 or 206,
3400 // since others can't really be turned into a 206.
3401 TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
3402 return OK;
3403 } else {
3404 partial_.reset();
3405 }
3406 }
3407
3408 if (!entry_->IsWritingInProgress()) {
3409 mode_ = READ;
3410 }
3411
3412 if (method_ == "HEAD") {
3413 FixHeadersForHead();
3414 }
3415
3416 TransitionToState(STATE_FINISH_HEADERS);
3417 return OK;
3418 }
3419
WriteResponseInfoToEntry(const HttpResponseInfo & response,bool truncated)3420 int HttpCache::Transaction::WriteResponseInfoToEntry(
3421 const HttpResponseInfo& response,
3422 bool truncated) {
3423 DCHECK(response.headers);
3424 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::WriteResponseInfoToEntry",
3425 perfetto::Track(trace_id_), "truncated", truncated);
3426
3427 if (!entry_) {
3428 return OK;
3429 }
3430
3431 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_WRITE_INFO);
3432
3433 // Do not cache content with cert errors. This is to prevent not reporting net
3434 // errors when loading a resource from the cache. When we load a page over
3435 // HTTPS with a cert error we show an SSL blocking page. If the user clicks
3436 // proceed we reload the resource ignoring the errors. The loaded resource is
3437 // then cached. If that resource is subsequently loaded from the cache, no
3438 // net error is reported (even though the cert status contains the actual
3439 // errors) and no SSL blocking page is shown. An alternative would be to
3440 // reverse-map the cert status to a net error and replay the net error.
3441 if (IsCertStatusError(response.ssl_info.cert_status) ||
3442 ShouldDisableCaching(*response.headers)) {
3443 if (partial_) {
3444 partial_->FixResponseHeaders(response_.headers.get(), true);
3445 }
3446
3447 bool stopped = StopCachingImpl(false);
3448 DCHECK(stopped);
3449 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_INFO,
3450 OK);
3451 return OK;
3452 }
3453
3454 if (truncated) {
3455 DCHECK_EQ(net::HTTP_OK, response.headers->response_code());
3456 }
3457
3458 // When writing headers, we normally only write the non-transient headers.
3459 bool skip_transient_headers = true;
3460 auto data = base::MakeRefCounted<PickledIOBuffer>();
3461 response.Persist(data->pickle(), skip_transient_headers, truncated);
3462 data->Done();
3463
3464 io_buf_len_ = data->pickle()->size();
3465
3466 // Summarize some info on cacheability in memory. Don't do it if doomed
3467 // since then |entry_| isn't definitive for |cache_key_|.
3468 if (!entry_->IsDoomed()) {
3469 cache_->GetCurrentBackend()->SetEntryInMemoryData(
3470 cache_key_, ComputeUnusablePerCachingHeaders()
3471 ? HINT_UNUSABLE_PER_CACHING_HEADERS
3472 : 0);
3473 }
3474
3475 BeginDiskCacheAccessTimeCount();
3476 return entry_->GetEntry()->WriteData(kResponseInfoIndex, 0, data.get(),
3477 io_buf_len_, io_callback_, true);
3478 }
3479
OnWriteResponseInfoToEntryComplete(int result)3480 int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) {
3481 TRACE_EVENT_INSTANT(
3482 "net", "HttpCacheTransaction::OnWriteResponseInfoToEntryComplete",
3483 perfetto::Track(trace_id_), "result", result);
3484 EndDiskCacheAccessTimeCount(DiskCacheAccessType::kWrite);
3485 if (!entry_) {
3486 return OK;
3487 }
3488 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_INFO,
3489 result);
3490
3491 if (result != io_buf_len_) {
3492 DLOG(ERROR) << "failed to write response info to cache";
3493 DoneWithEntry(false);
3494 }
3495 return OK;
3496 }
3497
StopCachingImpl(bool success)3498 bool HttpCache::Transaction::StopCachingImpl(bool success) {
3499 bool stopped = false;
3500 // Let writers know so that it doesn't attempt to write to the cache.
3501 if (InWriters()) {
3502 stopped = entry_->writers()->StopCaching(success /* keep_entry */);
3503 if (stopped) {
3504 mode_ = NONE;
3505 }
3506 } else if (entry_) {
3507 stopped = true;
3508 DoneWithEntry(success /* entry_is_complete */);
3509 }
3510 return stopped;
3511 }
3512
DoneWithEntry(bool entry_is_complete)3513 void HttpCache::Transaction::DoneWithEntry(bool entry_is_complete) {
3514 TRACE_EVENT_INSTANT("net", "HttpCacheTransaction::DoneWithEntry",
3515 perfetto::Track(trace_id_), "entry_is_complete",
3516 entry_is_complete);
3517 if (!entry_) {
3518 return;
3519 }
3520
3521 // Our `entry_` member must be valid throughout this call since
3522 // `DoneWithEntry` calls into
3523 // `HttpCache::Transaction::WriterAboutToBeRemovedFromEntry` which accesses
3524 // `this`'s `entry_` member.
3525 cache_->DoneWithEntry(entry_, this, entry_is_complete, partial_ != nullptr);
3526 entry_.reset();
3527 mode_ = NONE; // switch to 'pass through' mode
3528 }
3529
OnCacheReadError(int result,bool restart)3530 int HttpCache::Transaction::OnCacheReadError(int result, bool restart) {
3531 DLOG(ERROR) << "ReadData failed: " << result;
3532
3533 // Avoid using this entry in the future.
3534 if (cache_.get()) {
3535 cache_->DoomActiveEntry(cache_key_);
3536 }
3537
3538 if (restart) {
3539 DCHECK(!reading_);
3540 DCHECK(!network_trans_.get());
3541
3542 // Since we are going to add this to a new entry, not recording histograms
3543 // or setting mode to NONE at this point by invoking the wrapper
3544 // DoneWithEntry.
3545 //
3546 // Our `entry_` member must be valid throughout this call since
3547 // `DoneWithEntry` calls into
3548 // `HttpCache::Transaction::WriterAboutToBeRemovedFromEntry` which accesses
3549 // `this`'s `entry_` member.
3550 cache_->DoneWithEntry(entry_, this, true /* entry_is_complete */,
3551 partial_ != nullptr);
3552 entry_.reset();
3553 is_sparse_ = false;
3554 // It's OK to use PartialData::RestoreHeaders here as |restart| is only set
3555 // when the HttpResponseInfo couldn't even be read, at which point it's
3556 // too early for range info in |partial_| to have changed.
3557 if (partial_) {
3558 partial_->RestoreHeaders(&custom_request_->extra_headers);
3559 }
3560 partial_.reset();
3561 TransitionToState(STATE_GET_BACKEND);
3562 return OK;
3563 }
3564
3565 TransitionToState(STATE_NONE);
3566 return ERR_CACHE_READ_FAILURE;
3567 }
3568
OnCacheLockTimeout(base::TimeTicks start_time)3569 void HttpCache::Transaction::OnCacheLockTimeout(base::TimeTicks start_time) {
3570 if (entry_lock_waiting_since_ != start_time) {
3571 return;
3572 }
3573
3574 DCHECK(next_state_ == STATE_ADD_TO_ENTRY_COMPLETE ||
3575 next_state_ == STATE_FINISH_HEADERS_COMPLETE || waiting_for_cache_io_);
3576
3577 if (!cache_) {
3578 return;
3579 }
3580
3581 if (next_state_ == STATE_ADD_TO_ENTRY_COMPLETE || waiting_for_cache_io_) {
3582 cache_->RemovePendingTransaction(this);
3583 } else {
3584 DoneWithEntry(false /* entry_is_complete */);
3585 }
3586 OnCacheIOComplete(ERR_CACHE_LOCK_TIMEOUT);
3587 }
3588
DoomPartialEntry(bool delete_object)3589 void HttpCache::Transaction::DoomPartialEntry(bool delete_object) {
3590 DVLOG(2) << "DoomPartialEntry";
3591 if (entry_ && !entry_->IsDoomed()) {
3592 int rv = cache_->DoomEntry(cache_key_, nullptr);
3593 DCHECK_EQ(OK, rv);
3594 }
3595
3596 // Our `entry_` member must be valid throughout this call since
3597 // `DoneWithEntry` calls into
3598 // `HttpCache::Transaction::WriterAboutToBeRemovedFromEntry` which accesses
3599 // `this`'s `entry_` member.
3600 cache_->DoneWithEntry(entry_, this, false /* entry_is_complete */,
3601 partial_ != nullptr);
3602 entry_.reset();
3603 is_sparse_ = false;
3604 truncated_ = false;
3605 if (delete_object) {
3606 partial_.reset(nullptr);
3607 }
3608 }
3609
DoPartialCacheReadCompleted(int result)3610 int HttpCache::Transaction::DoPartialCacheReadCompleted(int result) {
3611 partial_->OnCacheReadCompleted(result);
3612
3613 if (result == 0 && mode_ == READ_WRITE) {
3614 // We need to move on to the next range.
3615 TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
3616 } else if (result < 0) {
3617 return OnCacheReadError(result, false);
3618 } else {
3619 TransitionToState(STATE_NONE);
3620 }
3621 return result;
3622 }
3623
DoRestartPartialRequest()3624 int HttpCache::Transaction::DoRestartPartialRequest() {
3625 // The stored data cannot be used. Get rid of it and restart this request.
3626 net_log_.AddEvent(NetLogEventType::HTTP_CACHE_RESTART_PARTIAL_REQUEST);
3627
3628 // WRITE + Doom + STATE_INIT_ENTRY == STATE_CREATE_ENTRY (without an attempt
3629 // to Doom the entry again).
3630 ResetPartialState(!range_requested_);
3631
3632 // Change mode to WRITE after ResetPartialState as that may have changed the
3633 // mode to NONE.
3634 mode_ = WRITE;
3635 TransitionToState(STATE_CREATE_ENTRY);
3636 return OK;
3637 }
3638
ResetPartialState(bool delete_object)3639 void HttpCache::Transaction::ResetPartialState(bool delete_object) {
3640 partial_->RestoreHeaders(&custom_request_->extra_headers);
3641 DoomPartialEntry(delete_object);
3642
3643 if (!delete_object) {
3644 // The simplest way to re-initialize partial_ is to create a new object.
3645 partial_ = std::make_unique<PartialData>();
3646
3647 // Reset the range header to the original value (http://crbug.com/820599).
3648 custom_request_->extra_headers.RemoveHeader(HttpRequestHeaders::kRange);
3649 if (partial_->Init(initial_request_->extra_headers)) {
3650 partial_->SetHeaders(custom_request_->extra_headers);
3651 } else {
3652 partial_.reset();
3653 }
3654 }
3655 }
3656
ResetNetworkTransaction()3657 void HttpCache::Transaction::ResetNetworkTransaction() {
3658 SaveNetworkTransactionInfo(*network_trans_);
3659 network_trans_.reset();
3660 }
3661
network_transaction() const3662 const HttpTransaction* HttpCache::Transaction::network_transaction() const {
3663 if (network_trans_) {
3664 return network_trans_.get();
3665 }
3666 if (InWriters()) {
3667 return entry_->writers()->network_transaction();
3668 }
3669 return nullptr;
3670 }
3671
3672 const HttpTransaction*
GetOwnedOrMovedNetworkTransaction() const3673 HttpCache::Transaction::GetOwnedOrMovedNetworkTransaction() const {
3674 if (network_trans_) {
3675 return network_trans_.get();
3676 }
3677 if (InWriters() && moved_network_transaction_to_writers_) {
3678 return entry_->writers()->network_transaction();
3679 }
3680 return nullptr;
3681 }
3682
network_transaction()3683 HttpTransaction* HttpCache::Transaction::network_transaction() {
3684 return const_cast<HttpTransaction*>(
3685 static_cast<const Transaction*>(this)->network_transaction());
3686 }
3687
3688 // Histogram data from the end of 2010 show the following distribution of
3689 // response headers:
3690 //
3691 // Content-Length............... 87%
3692 // Date......................... 98%
3693 // Last-Modified................ 49%
3694 // Etag......................... 19%
3695 // Accept-Ranges: bytes......... 25%
3696 // Accept-Ranges: none.......... 0.4%
3697 // Strong Validator............. 50%
3698 // Strong Validator + ranges.... 24%
3699 // Strong Validator + CL........ 49%
3700 //
CanResume(bool has_data)3701 bool HttpCache::Transaction::CanResume(bool has_data) {
3702 // Double check that there is something worth keeping.
3703 if (has_data && !entry_->GetEntry()->GetDataSize(kResponseContentIndex)) {
3704 return false;
3705 }
3706
3707 if (method_ != "GET") {
3708 return false;
3709 }
3710
3711 // Note that if this is a 206, content-length was already fixed after calling
3712 // PartialData::ResponseHeadersOK().
3713 if (response_.headers->GetContentLength() <= 0 ||
3714 response_.headers->HasHeaderValue("Accept-Ranges", "none") ||
3715 !response_.headers->HasStrongValidators()) {
3716 return false;
3717 }
3718
3719 return true;
3720 }
3721
SetResponse(const HttpResponseInfo & response)3722 void HttpCache::Transaction::SetResponse(const HttpResponseInfo& response) {
3723 response_ = response;
3724
3725 if (response_.headers) {
3726 DCHECK(request_);
3727 response_.vary_data.Init(*request_, *response_.headers);
3728 }
3729
3730 SyncCacheEntryStatusToResponse();
3731 }
3732
SetAuthResponse(const HttpResponseInfo & auth_response)3733 void HttpCache::Transaction::SetAuthResponse(
3734 const HttpResponseInfo& auth_response) {
3735 auth_response_ = auth_response;
3736 SyncCacheEntryStatusToResponse();
3737 }
3738
UpdateCacheEntryStatus(CacheEntryStatus new_cache_entry_status)3739 void HttpCache::Transaction::UpdateCacheEntryStatus(
3740 CacheEntryStatus new_cache_entry_status) {
3741 DCHECK_NE(CacheEntryStatus::ENTRY_UNDEFINED, new_cache_entry_status);
3742 if (cache_entry_status_ == CacheEntryStatus::ENTRY_OTHER) {
3743 return;
3744 }
3745 DCHECK(cache_entry_status_ == CacheEntryStatus::ENTRY_UNDEFINED ||
3746 new_cache_entry_status == CacheEntryStatus::ENTRY_OTHER);
3747 cache_entry_status_ = new_cache_entry_status;
3748 SyncCacheEntryStatusToResponse();
3749 }
3750
SyncCacheEntryStatusToResponse()3751 void HttpCache::Transaction::SyncCacheEntryStatusToResponse() {
3752 if (cache_entry_status_ == CacheEntryStatus::ENTRY_UNDEFINED) {
3753 return;
3754 }
3755 response_.cache_entry_status = cache_entry_status_;
3756 if (auth_response_.headers.get()) {
3757 auth_response_.cache_entry_status = cache_entry_status_;
3758 }
3759 }
3760
RecordHistograms()3761 void HttpCache::Transaction::RecordHistograms() {
3762 DCHECK(!recorded_histograms_);
3763 recorded_histograms_ = true;
3764
3765 if (CacheEntryStatus::ENTRY_UNDEFINED == cache_entry_status_) {
3766 return;
3767 }
3768
3769 if (!cache_.get() || !cache_->GetCurrentBackend() ||
3770 cache_->GetCurrentBackend()->GetCacheType() != DISK_CACHE ||
3771 cache_->mode() != NORMAL || method_ != "GET") {
3772 return;
3773 }
3774
3775 bool is_third_party = false;
3776
3777 // Given that cache_entry_status_ is not ENTRY_UNDEFINED, the request must
3778 // have started and so request_ should exist.
3779 DCHECK(request_);
3780 if (request_->possibly_top_frame_origin) {
3781 is_third_party =
3782 !request_->possibly_top_frame_origin->IsSameOriginWith(request_->url);
3783 }
3784
3785 std::string mime_type;
3786 HttpResponseHeaders* response_headers = GetResponseInfo()->headers.get();
3787 const bool is_no_store = response_headers && response_headers->HasHeaderValue(
3788 "cache-control", "no-store");
3789 if (response_headers && response_headers->GetMimeType(&mime_type)) {
3790 // Record the cache pattern by resource type. The type is inferred by
3791 // response header mime type, which could be incorrect, so this is just an
3792 // estimate.
3793 if (mime_type == "text/html" &&
3794 (effective_load_flags_ & LOAD_MAIN_FRAME_DEPRECATED)) {
3795 CACHE_STATUS_HISTOGRAMS(".MainFrameHTML");
3796 IS_NO_STORE_HISTOGRAMS(".MainFrameHTML", is_no_store);
3797 } else if (mime_type == "text/html") {
3798 CACHE_STATUS_HISTOGRAMS(".NonMainFrameHTML");
3799 } else if (mime_type == "text/css") {
3800 if (is_third_party) {
3801 CACHE_STATUS_HISTOGRAMS(".CSSThirdParty");
3802 }
3803 CACHE_STATUS_HISTOGRAMS(".CSS");
3804 } else if (mime_type.starts_with("image/")) {
3805 int64_t content_length = response_headers->GetContentLength();
3806 if (content_length >= 0 && content_length < 100) {
3807 CACHE_STATUS_HISTOGRAMS(".TinyImage");
3808 } else if (content_length >= 100) {
3809 CACHE_STATUS_HISTOGRAMS(".NonTinyImage");
3810 }
3811 CACHE_STATUS_HISTOGRAMS(".Image");
3812 } else if (mime_type.ends_with("javascript") ||
3813 mime_type.ends_with("ecmascript")) {
3814 if (is_third_party) {
3815 CACHE_STATUS_HISTOGRAMS(".JavaScriptThirdParty");
3816 }
3817 CACHE_STATUS_HISTOGRAMS(".JavaScript");
3818 } else if (mime_type.find("font") != std::string::npos) {
3819 if (is_third_party) {
3820 CACHE_STATUS_HISTOGRAMS(".FontThirdParty");
3821 }
3822 CACHE_STATUS_HISTOGRAMS(".Font");
3823 } else if (mime_type.starts_with("audio/")) {
3824 CACHE_STATUS_HISTOGRAMS(".Audio");
3825 } else if (mime_type.starts_with("video/")) {
3826 CACHE_STATUS_HISTOGRAMS(".Video");
3827 }
3828 }
3829
3830 CACHE_STATUS_HISTOGRAMS("");
3831 IS_NO_STORE_HISTOGRAMS("", is_no_store);
3832
3833 if (cache_entry_status_ == CacheEntryStatus::ENTRY_OTHER) {
3834 return;
3835 }
3836
3837 DCHECK(!range_requested_) << "Cache entry status " << cache_entry_status_;
3838 DCHECK(!first_cache_access_since_.is_null());
3839
3840 base::TimeTicks now = base::TimeTicks::Now();
3841 base::TimeDelta total_time = now - first_cache_access_since_;
3842
3843 UMA_HISTOGRAM_CUSTOM_TIMES("HttpCache.AccessToDone2", total_time,
3844 base::Milliseconds(1), base::Seconds(30), 100);
3845
3846 bool did_send_request = !send_request_since_.is_null();
3847
3848 // It's not clear why `did_send_request` can be true when status is
3849 // ENTRY_USED. See https://crbug.com/1409150.
3850 // TODO(ricea): Maybe remove ENTRY_USED from the `did_send_request` true
3851 // branch once that issue is resolved.
3852 DCHECK(
3853 (did_send_request &&
3854 (cache_entry_status_ == CacheEntryStatus::ENTRY_NOT_IN_CACHE ||
3855 cache_entry_status_ == CacheEntryStatus::ENTRY_VALIDATED ||
3856 cache_entry_status_ == CacheEntryStatus::ENTRY_UPDATED ||
3857 cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE ||
3858 cache_entry_status_ == CacheEntryStatus::ENTRY_USED)) ||
3859 (!did_send_request &&
3860 (cache_entry_status_ == CacheEntryStatus::ENTRY_USED ||
3861 cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE)));
3862
3863 if (!did_send_request) {
3864 if (cache_entry_status_ == CacheEntryStatus::ENTRY_USED) {
3865 UMA_HISTOGRAM_CUSTOM_TIMES("HttpCache.AccessToDone2.Used", total_time,
3866 base::Milliseconds(1), base::Seconds(3), 100);
3867 }
3868 return;
3869 }
3870
3871 base::TimeDelta before_send_time =
3872 send_request_since_ - first_cache_access_since_;
3873
3874 UMA_HISTOGRAM_CUSTOM_TIMES("HttpCache.AccessToDone2.SentRequest", total_time,
3875 base::Milliseconds(1), base::Seconds(30), 100);
3876 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend", before_send_time);
3877
3878 // TODO(gavinp): Remove or minimize these histograms, particularly the ones
3879 // below this comment after we have received initial data.
3880 switch (cache_entry_status_) {
3881 case CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE: {
3882 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.CantConditionalize",
3883 before_send_time);
3884 break;
3885 }
3886 case CacheEntryStatus::ENTRY_NOT_IN_CACHE: {
3887 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.NotCached", before_send_time);
3888 break;
3889 }
3890 case CacheEntryStatus::ENTRY_VALIDATED: {
3891 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Validated", before_send_time);
3892 break;
3893 }
3894 case CacheEntryStatus::ENTRY_UPDATED: {
3895 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Updated", before_send_time);
3896 break;
3897 }
3898 default:
3899 // STATUS_UNDEFINED and STATUS_OTHER are explicitly handled earlier in
3900 // the function so shouldn't reach here. STATUS_MAX should never be set.
3901 // Originally it was asserted that STATUS_USED couldn't happen here, but
3902 // it turns out that it can. We don't have histograms for it, so just
3903 // ignore it.
3904 DCHECK_EQ(cache_entry_status_, CacheEntryStatus::ENTRY_USED);
3905 break;
3906 }
3907
3908 if (!total_disk_cache_read_time_.is_zero()) {
3909 base::UmaHistogramTimes("HttpCache.TotalDiskCacheTimePerTransaction.Read",
3910 total_disk_cache_read_time_);
3911 }
3912 if (!total_disk_cache_write_time_.is_zero()) {
3913 base::UmaHistogramTimes("HttpCache.TotalDiskCacheTimePerTransaction.Write",
3914 total_disk_cache_write_time_);
3915 }
3916 }
3917
InWriters() const3918 bool HttpCache::Transaction::InWriters() const {
3919 return entry_ && entry_->HasWriters() &&
3920 entry_->writers()->HasTransaction(this);
3921 }
3922
3923 HttpCache::Transaction::NetworkTransactionInfo::NetworkTransactionInfo() =
3924 default;
3925 HttpCache::Transaction::NetworkTransactionInfo::~NetworkTransactionInfo() =
3926 default;
3927
SaveNetworkTransactionInfo(const HttpTransaction & transaction)3928 void HttpCache::Transaction::SaveNetworkTransactionInfo(
3929 const HttpTransaction& transaction) {
3930 DCHECK(!network_transaction_info_.old_network_trans_load_timing);
3931 LoadTimingInfo load_timing;
3932 if (transaction.GetLoadTimingInfo(&load_timing)) {
3933 network_transaction_info_.old_network_trans_load_timing =
3934 std::make_unique<LoadTimingInfo>(load_timing);
3935 }
3936
3937 network_transaction_info_.total_received_bytes +=
3938 transaction.GetTotalReceivedBytes();
3939 network_transaction_info_.total_sent_bytes += transaction.GetTotalSentBytes();
3940
3941 ConnectionAttempts attempts = transaction.GetConnectionAttempts();
3942 for (const auto& attempt : attempts) {
3943 network_transaction_info_.old_connection_attempts.push_back(attempt);
3944 }
3945 network_transaction_info_.old_remote_endpoint = IPEndPoint();
3946 transaction.GetRemoteEndpoint(&network_transaction_info_.old_remote_endpoint);
3947
3948 if (transaction.IsMdlMatchForMetrics()) {
3949 network_transaction_info_.previous_mdl_match_for_metrics = true;
3950 }
3951 }
3952
OnIOComplete(int result)3953 void HttpCache::Transaction::OnIOComplete(int result) {
3954 if (waiting_for_cache_io_) {
3955 CHECK_NE(result, ERR_CACHE_RACE);
3956 // If the HttpCache IO hasn't completed yet, queue the IO result
3957 // to be processed when the HttpCache IO completes (or times out).
3958 pending_io_result_ = result;
3959 } else {
3960 DoLoop(result);
3961 }
3962 }
3963
OnCacheIOComplete(int result)3964 void HttpCache::Transaction::OnCacheIOComplete(int result) {
3965 if (waiting_for_cache_io_) {
3966 // Handle the case of parallel HttpCache transactions being run against
3967 // network IO.
3968 waiting_for_cache_io_ = false;
3969 cache_pending_ = false;
3970 entry_lock_waiting_since_ = TimeTicks();
3971
3972 if (result == OK) {
3973 entry_ = std::move(new_entry_);
3974 if (!entry_->IsWritingInProgress()) {
3975 open_entry_last_used_ = entry_->GetEntry()->GetLastUsed();
3976 }
3977 } else {
3978 // The HttpCache transaction failed or timed out. Bypass the cache in
3979 // this case independent of the state of the network IO callback.
3980 mode_ = NONE;
3981 }
3982 new_entry_.reset();
3983
3984 // See if there is a pending IO result that completed while the HttpCache
3985 // transaction was being processed that now needs to be processed.
3986 if (pending_io_result_) {
3987 int stored_result = pending_io_result_.value();
3988 pending_io_result_ = std::nullopt;
3989 OnIOComplete(stored_result);
3990 }
3991 } else {
3992 DoLoop(result);
3993 }
3994 }
3995
TransitionToState(State state)3996 void HttpCache::Transaction::TransitionToState(State state) {
3997 // Ensure that the state is only set once per Do* state.
3998 DCHECK(in_do_loop_);
3999 DCHECK_EQ(STATE_UNSET, next_state_) << "Next state is " << state;
4000 next_state_ = state;
4001 }
4002
ShouldDisableCaching(const HttpResponseHeaders & headers) const4003 bool HttpCache::Transaction::ShouldDisableCaching(
4004 const HttpResponseHeaders& headers) const {
4005 // Do not cache no-store content.
4006 if (headers.HasHeaderValue("cache-control", "no-store")) {
4007 return true;
4008 }
4009
4010 bool disable_caching = false;
4011 if (base::FeatureList::IsEnabled(
4012 features::kTurnOffStreamingMediaCachingAlways) ||
4013 (base::FeatureList::IsEnabled(
4014 features::kTurnOffStreamingMediaCachingOnBattery) &&
4015 IsOnBatteryPower())) {
4016 // If the feature is always enabled or enabled while we're running on
4017 // battery, and the acquired content is 'large' and not already cached, and
4018 // we have a MIME type of audio or video, then disable the cache for this
4019 // response. We based our initial definition of 'large' on the disk cache
4020 // maximum block size of 16K, which we observed captures the majority of
4021 // responses from various MSE implementations.
4022 static constexpr int kMaxContentSize = 4096 * 4;
4023 std::string mime_type;
4024 base::CompareCase insensitive_ascii = base::CompareCase::INSENSITIVE_ASCII;
4025 if (headers.GetContentLength() > kMaxContentSize &&
4026 headers.response_code() != net::HTTP_NOT_MODIFIED &&
4027 headers.GetMimeType(&mime_type) &&
4028 (base::StartsWith(mime_type, "video", insensitive_ascii) ||
4029 base::StartsWith(mime_type, "audio", insensitive_ascii))) {
4030 disable_caching = true;
4031 }
4032 }
4033 return disable_caching;
4034 }
4035
UpdateSecurityHeadersBeforeForwarding()4036 void HttpCache::Transaction::UpdateSecurityHeadersBeforeForwarding() {
4037 // Because of COEP, we need to add CORP to the 304 of resources that set it
4038 // previously. It will be blocked in the network service otherwise.
4039 std::string stored_corp_header;
4040 response_.headers->GetNormalizedHeader("Cross-Origin-Resource-Policy",
4041 &stored_corp_header);
4042 if (!stored_corp_header.empty()) {
4043 new_response_->headers->SetHeader("Cross-Origin-Resource-Policy",
4044 stored_corp_header);
4045 }
4046 return;
4047 }
4048
BeginDiskCacheAccessTimeCount()4049 void HttpCache::Transaction::BeginDiskCacheAccessTimeCount() {
4050 DCHECK(last_disk_cache_access_start_time_.is_null());
4051 if (partial_) {
4052 return;
4053 }
4054 last_disk_cache_access_start_time_ = TimeTicks::Now();
4055 }
4056
EndDiskCacheAccessTimeCount(DiskCacheAccessType type)4057 void HttpCache::Transaction::EndDiskCacheAccessTimeCount(
4058 DiskCacheAccessType type) {
4059 // We may call this function without actual disk cache access as a result of
4060 // state change.
4061 if (last_disk_cache_access_start_time_.is_null()) {
4062 return;
4063 }
4064 base::TimeDelta elapsed =
4065 TimeTicks::Now() - last_disk_cache_access_start_time_;
4066 switch (type) {
4067 case DiskCacheAccessType::kRead:
4068 total_disk_cache_read_time_ += elapsed;
4069 break;
4070 case DiskCacheAccessType::kWrite:
4071 total_disk_cache_write_time_ += elapsed;
4072 break;
4073 }
4074 last_disk_cache_access_start_time_ = TimeTicks();
4075 }
4076
4077 } // namespace net
4078