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