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