• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/http/http_cache_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