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