#include "net/http/http_cache_transaction.h"
#include "base/feature_list.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_POSIX)
#include <unistd.h>
#endif
#include <algorithm>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "base/auto_reset.h"
#include "base/compiler_specific.h"
#include "base/containers/fixed_flat_set.h"
#include "base/cxx17_backports.h"
#include "base/format_macros.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/power_monitor/power_monitor.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/clock.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/values.h"
#include "crypto/secure_hash.h"
#include "crypto/sha2.h"
#include "net/base/auth.h"
#include "net/base/cache_metrics.h"
#include "net/base/features.h"
#include "net/base/load_flags.h"
#include "net/base/load_timing_info.h"
#include "net/base/trace_constants.h"
#include "net/base/tracing.h"
#include "net/base/transport_info.h"
#include "net/base/upload_data_stream.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/x509_certificate.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache_writers.h"
#include "net/http/http_log_util.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/http/webfonts_histogram.h"
#include "net/log/net_log_event_type.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_config_service.h"
#if BUILDFLAG(IS_OHOS)
#include "base/ohos/sys_info_utils.h"
#include "base/logging.h"
#include "ohos_adapter_helper.h"
#endif
#ifdef OHOS_LOGGER_REPORT
#include "url/ohos/log_utils.h"
#endif
using base::Time;
using base::TimeTicks;
namespace net {
using CacheEntryStatus = HttpResponseInfo::CacheEntryStatus;
namespace {
constexpr base::TimeDelta kStaleRevalidateTimeout = base::Seconds(60);
uint64_t GetNextTraceId(HttpCache* cache) {
static uint32_t sNextTraceId = 0;
DCHECK(cache);
return (reinterpret_cast<uint64_t>(cache) << 32) | sNextTraceId++;
}
bool NonErrorResponse(int status_code) {
int status_code_range = status_code / 100;
return status_code_range == 2 || status_code_range == 3;
}
bool IsOnBatteryPower() {
if (base::PowerMonitor::IsInitialized())
return base::PowerMonitor::IsOnBatteryPower();
return false;
}
enum ExternallyConditionalizedType {
EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION,
EXTERNALLY_CONDITIONALIZED_CACHE_USABLE,
EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS,
EXTERNALLY_CONDITIONALIZED_MAX
};
enum class RestrictedPrefetchReused {
kNotReused = 0,
kReused = 1,
kMaxValue = kReused
};
void RecordPervasivePayloadIndex(const char* histogram_name, int index) {
if (index != -1) {
base::UmaHistogramCustomCounts(histogram_name, index, 1, 323, 323);
}
}
bool ShouldByPassCacheForFirstPartySets(
const absl::optional<int64_t>& clear_at_run_id,
const absl::optional<int64_t>& written_at_run_id) {
return clear_at_run_id.has_value() &&
(!written_at_run_id.has_value() ||
written_at_run_id.value() < clear_at_run_id.value());
}
}
#define CACHE_STATUS_HISTOGRAMS(type) \
UMA_HISTOGRAM_ENUMERATION("HttpCache.Pattern" type, cache_entry_status_, \
CacheEntryStatus::ENTRY_MAX)
#define IS_NO_STORE_HISTOGRAMS(type, is_no_store) \
base::UmaHistogramBoolean("HttpCache.IsNoStore" type, is_no_store)
struct HeaderNameAndValue {
const char* name;
const char* value;
};
static const HeaderNameAndValue kPassThroughHeaders[] = {
{"if-unmodified-since", nullptr},
{"if-match", nullptr},
{"if-range", nullptr},
{nullptr, nullptr}};
struct ValidationHeaderInfo {
const char* request_header_name;
const char* related_response_header_name;
};
static const ValidationHeaderInfo kValidationHeaders[] = {
{ "if-modified-since", "last-modified" },
{ "if-none-match", "etag" },
};
static const HeaderNameAndValue kForceFetchHeaders[] = {
{"cache-control", "no-cache"},
{"pragma", "no-cache"},
{nullptr, nullptr}};
static const HeaderNameAndValue kForceValidateHeaders[] = {
{"cache-control", "max-age=0"},
{nullptr, nullptr}};
static bool HeaderMatches(const HttpRequestHeaders& headers,
const HeaderNameAndValue* search) {
for (; search->name; ++search) {
std::string header_value;
if (!headers.GetHeader(search->name, &header_value))
continue;
if (!search->value)
return true;
HttpUtil::ValuesIterator v(header_value.begin(), header_value.end(), ',');
while (v.GetNext()) {
if (base::EqualsCaseInsensitiveASCII(v.value_piece(), search->value))
return true;
}
}
return false;
}
HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache)
: trace_id_(GetNextTraceId(cache)),
priority_(priority),
cache_(cache->GetWeakPtr()) {
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::Transaction",
TRACE_ID_LOCAL(trace_id_), TRACE_EVENT_FLAG_FLOW_OUT,
"priority", RequestPriorityToString(priority));
static_assert(HttpCache::Transaction::kNumValidationHeaders ==
std::size(kValidationHeaders),
"invalid number of validation headers");
io_callback_ = base::BindRepeating(&Transaction::OnIOComplete,
weak_factory_.GetWeakPtr());
}
HttpCache::Transaction::~Transaction() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::~Transaction",
TRACE_ID_LOCAL(trace_id_), TRACE_EVENT_FLAG_FLOW_IN);
RecordHistograms();
callback_.Reset();
if (cache_) {
if (entry_) {
DoneWithEntry(false );
} else if (cache_pending_) {
cache_->RemovePendingTransaction(this);
}
}
}
HttpCache::Transaction::Mode HttpCache::Transaction::mode() const {
return mode_;
}
LoadState HttpCache::Transaction::GetWriterLoadState() const {
const HttpTransaction* transaction = network_transaction();
if (transaction)
return transaction->GetLoadState();
if (entry_ || !request_)
return LOAD_STATE_IDLE;
return LOAD_STATE_WAITING_FOR_CACHE;
}
const NetLogWithSource& HttpCache::Transaction::net_log() const {
return net_log_;
}
int HttpCache::Transaction::Start(const HttpRequestInfo* request,
CompletionOnceCallback callback,
const NetLogWithSource& net_log) {
DCHECK(request);
DCHECK(request->IsConsistent());
DCHECK(!callback.is_null());
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::Start",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"url", request->url.spec());
DCHECK(callback_.is_null());
DCHECK(!reading_);
DCHECK(!network_trans_.get());
DCHECK(!entry_);
DCHECK_EQ(next_state_, STATE_NONE);
if (!cache_.get())
return ERR_UNEXPECTED;
initial_request_ = request;
SetRequest(net_log);
next_state_ = STATE_GET_BACKEND;
#if BUILDFLAG(IS_OHOS_PRPP)
if (!update_res_request_info_callback_.is_null() && request_ && preload_info_) {
update_res_request_info_callback_.Run(request_->main_url.spec(), preload_info_);
}
#endif
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
callback_ = std::move(callback);
return rv;
}
#ifdef OHOS_EX_HTTP_DNS_FALLBACK
int HttpCache::Transaction::RestartWithSecureDnsOnly(
CompletionOnceCallback callback) {
DCHECK(callback_.is_null());
if (!cache_.get()) {
return ERR_UNEXPECTED;
}
int rv = RestartNetworkRequestWithSecureDnsOnly();
if (rv == ERR_IO_PENDING) {
callback_ = std::move(callback);
}
return rv;
}
int HttpCache::Transaction::RestartNetworkRequestWithSecureDnsOnly() {
DCHECK(mode_ & WRITE || mode_ == NONE);
DCHECK(network_trans_.get());
DCHECK_EQ(STATE_NONE, next_state_);
next_state_ = STATE_SEND_REQUEST_COMPLETE;
if (request_ != initial_request_ && custom_request_) {
custom_request_->secure_dns_only = true;
}
int rv = network_trans_->RestartWithSecureDnsOnly(io_callback_);
if (rv != ERR_IO_PENDING) {
return DoLoop(rv);
}
return rv;
}
#endif
int HttpCache::Transaction::RestartIgnoringLastError(
CompletionOnceCallback callback) {
DCHECK(!callback.is_null());
DCHECK(callback_.is_null());
if (!cache_.get())
return ERR_UNEXPECTED;
int rv = RestartNetworkRequest();
if (rv == ERR_IO_PENDING)
callback_ = std::move(callback);
return rv;
}
int HttpCache::Transaction::RestartWithCertificate(
scoped_refptr<X509Certificate> client_cert,
scoped_refptr<SSLPrivateKey> client_private_key,
CompletionOnceCallback callback) {
DCHECK(!callback.is_null());
DCHECK(callback_.is_null());
if (!cache_.get())
return ERR_UNEXPECTED;
int rv = RestartNetworkRequestWithCertificate(std::move(client_cert),
std::move(client_private_key));
if (rv == ERR_IO_PENDING)
callback_ = std::move(callback);
return rv;
}
int HttpCache::Transaction::RestartWithAuth(const AuthCredentials& credentials,
CompletionOnceCallback callback) {
DCHECK(auth_response_.headers.get());
DCHECK(!callback.is_null());
DCHECK(callback_.is_null());
if (!cache_.get())
return ERR_UNEXPECTED;
SetAuthResponse(HttpResponseInfo());
int rv = RestartNetworkRequestWithAuth(credentials);
if (rv == ERR_IO_PENDING)
callback_ = std::move(callback);
return rv;
}
bool HttpCache::Transaction::IsReadyToRestartForAuth() {
if (!network_trans_.get())
return false;
return network_trans_->IsReadyToRestartForAuth();
}
int HttpCache::Transaction::Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::Read", TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "buf_len", buf_len);
DCHECK_EQ(next_state_, STATE_NONE);
DCHECK(buf);
CHECK_GT(buf_len, 0);
DCHECK(!callback.is_null());
DCHECK(callback_.is_null());
if (!cache_.get())
return ERR_UNEXPECTED;
if (auth_response_.headers.get() && mode_ != NONE) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
DCHECK(mode_ & WRITE);
bool stopped = StopCachingImpl(mode_ == READ_WRITE);
DCHECK(stopped);
}
reading_ = true;
read_buf_ = buf;
read_buf_len_ = buf_len;
int rv = TransitionToReadingState();
if (rv != OK || next_state_ == STATE_NONE)
return rv;
rv = DoLoop(OK);
if (rv == ERR_IO_PENDING) {
DCHECK(callback_.is_null());
callback_ = std::move(callback);
}
return rv;
}
int HttpCache::Transaction::TransitionToReadingState() {
if (!entry_) {
if (network_trans_) {
next_state_ = STATE_NETWORK_READ;
return OK;
}
next_state_ = STATE_NONE;
return shared_writing_error_;
}
if (!InWriters()) {
DCHECK(entry_->TransactionInReaders(this));
DCHECK(mode_ == READ || (mode_ == READ_WRITE && partial_));
next_state_ = STATE_CACHE_READ_DATA;
return OK;
}
DCHECK(mode_ & WRITE || mode_ == NONE);
if (partial_) {
if (entry_->writers->network_transaction())
next_state_ = STATE_NETWORK_READ_CACHE_WRITE;
else
next_state_ = STATE_CACHE_READ_DATA;
return OK;
}
int disk_entry_size = entry_->GetEntry()->GetDataSize(kResponseContentIndex);
if (read_offset_ == disk_entry_size || entry_->writers->network_read_only()) {
next_state_ = STATE_NETWORK_READ_CACHE_WRITE;
} else {
DCHECK_LT(read_offset_, disk_entry_size);
next_state_ = STATE_CACHE_READ_DATA;
}
return OK;
}
void HttpCache::Transaction::StopCaching() {
if (cache_.get() && (mode_ & WRITE) && !is_sparse_ && !range_requested_ &&
network_transaction()) {
StopCachingImpl(false);
}
}
int64_t HttpCache::Transaction::GetTotalReceivedBytes() const {
int64_t total_received_bytes = network_transaction_info_.total_received_bytes;
const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
if (transaction)
total_received_bytes += transaction->GetTotalReceivedBytes();
return total_received_bytes;
}
int64_t HttpCache::Transaction::GetTotalSentBytes() const {
int64_t total_sent_bytes = network_transaction_info_.total_sent_bytes;
const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
if (transaction)
total_sent_bytes += transaction->GetTotalSentBytes();
return total_sent_bytes;
}
void HttpCache::Transaction::DoneReading() {
if (cache_.get() && entry_) {
DCHECK_NE(mode_, UPDATE);
DoneWithEntry(true);
}
}
const HttpResponseInfo* HttpCache::Transaction::GetResponseInfo() const {
if (auth_response_.headers.get()) {
DCHECK_EQ(cache_entry_status_, auth_response_.cache_entry_status)
<< "These must be in sync via SetResponse and SetAuthResponse.";
return &auth_response_;
}
return &response_;
}
LoadState HttpCache::Transaction::GetLoadState() const {
if (!callback_)
return LOAD_STATE_IDLE;
LoadState state = GetWriterLoadState();
if (state != LOAD_STATE_WAITING_FOR_CACHE)
return state;
if (cache_.get())
return cache_->GetLoadStateForPendingTransaction(this);
return LOAD_STATE_IDLE;
}
void HttpCache::Transaction::SetQuicServerInfo(
QuicServerInfo* quic_server_info) {}
bool HttpCache::Transaction::GetLoadTimingInfo(
LoadTimingInfo* load_timing_info) const {
const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
if (transaction)
return transaction->GetLoadTimingInfo(load_timing_info);
if (network_transaction_info_.old_network_trans_load_timing) {
*load_timing_info =
*network_transaction_info_.old_network_trans_load_timing;
return true;
}
if (first_cache_access_since_.is_null())
return false;
load_timing_info->send_start = first_cache_access_since_;
load_timing_info->send_end = first_cache_access_since_;
load_timing_info->receive_headers_start = read_headers_since_;
return true;
}
bool HttpCache::Transaction::GetRemoteEndpoint(IPEndPoint* endpoint) const {
const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
if (transaction)
return transaction->GetRemoteEndpoint(endpoint);
if (!network_transaction_info_.old_remote_endpoint.address().empty()) {
*endpoint = network_transaction_info_.old_remote_endpoint;
return true;
}
return false;
}
void HttpCache::Transaction::PopulateNetErrorDetails(
NetErrorDetails* details) const {
const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
if (transaction)
return transaction->PopulateNetErrorDetails(details);
return;
}
void HttpCache::Transaction::SetPriority(RequestPriority priority) {
priority_ = priority;
if (network_trans_)
network_trans_->SetPriority(priority_);
if (InWriters()) {
DCHECK(!network_trans_ || partial_);
entry_->writers->UpdatePriority();
}
}
void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper(
WebSocketHandshakeStreamBase::CreateHelper* create_helper) {
websocket_handshake_stream_base_create_helper_ = create_helper;
HttpTransaction* transaction = network_transaction();
if (transaction)
transaction->SetWebSocketHandshakeStreamCreateHelper(create_helper);
}
void HttpCache::Transaction::SetBeforeNetworkStartCallback(
BeforeNetworkStartCallback callback) {
DCHECK(!network_trans_);
before_network_start_callback_ = std::move(callback);
}
void HttpCache::Transaction::SetConnectedCallback(
const ConnectedCallback& callback) {
DCHECK(!network_trans_);
connected_callback_ = callback;
}
void HttpCache::Transaction::SetRequestHeadersCallback(
RequestHeadersCallback callback) {
DCHECK(!network_trans_);
request_headers_callback_ = std::move(callback);
}
void HttpCache::Transaction::SetResponseHeadersCallback(
ResponseHeadersCallback callback) {
DCHECK(!network_trans_);
response_headers_callback_ = std::move(callback);
}
void HttpCache::Transaction::SetEarlyResponseHeadersCallback(
ResponseHeadersCallback callback) {
DCHECK(!network_trans_);
early_response_headers_callback_ = std::move(callback);
}
int HttpCache::Transaction::ResumeNetworkStart() {
if (network_trans_)
return network_trans_->ResumeNetworkStart();
return ERR_UNEXPECTED;
}
ConnectionAttempts HttpCache::Transaction::GetConnectionAttempts() const {
ConnectionAttempts attempts;
const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction();
if (transaction)
attempts = transaction->GetConnectionAttempts();
attempts.insert(attempts.begin(),
network_transaction_info_.old_connection_attempts.begin(),
network_transaction_info_.old_connection_attempts.end());
return attempts;
}
void HttpCache::Transaction::CloseConnectionOnDestruction() {
if (network_trans_) {
network_trans_->CloseConnectionOnDestruction();
} else if (InWriters()) {
entry_->writers->CloseConnectionOnDestruction();
}
}
void HttpCache::Transaction::SetValidatingCannotProceed() {
DCHECK(!reading_);
DCHECK_NE(STATE_UNSET, next_state_);
next_state_ = STATE_HEADERS_PHASE_CANNOT_PROCEED;
entry_ = nullptr;
}
void HttpCache::Transaction::WriterAboutToBeRemovedFromEntry(int result) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::WriterAboutToBeRemovedFromEntry",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result);
if (moved_network_transaction_to_writers_ &&
entry_->writers->network_transaction()) {
SaveNetworkTransactionInfo(*(entry_->writers->network_transaction()));
}
entry_ = nullptr;
mode_ = NONE;
if (result < 0)
shared_writing_error_ = result;
}
void HttpCache::Transaction::WriteModeTransactionAboutToBecomeReader() {
TRACE_EVENT_WITH_FLOW0(
"net", "HttpCacheTransaction::WriteModeTransactionAboutToBecomeReader",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
mode_ = READ;
if (moved_network_transaction_to_writers_ &&
entry_->writers->network_transaction()) {
SaveNetworkTransactionInfo(*(entry_->writers->network_transaction()));
}
}
bool HttpCache::Transaction::ResponseChecksumMatches(
std::unique_ptr<crypto::SecureHash> checksum) const {
DCHECK(checksum);
uint8_t result[crypto::kSHA256Length];
checksum->Finish(result, crypto::kSHA256Length);
const std::string hex_result = base::HexEncode(result);
if (hex_result != request_->checksum) {
DVLOG(2) << "Pervasive payload checksum mismatch for \"" << request_->url
<< "\": got " << hex_result << ", expected " << request_->checksum;
RecordPervasivePayloadIndex(
"Network.CacheTransparency2.MismatchedChecksums",
request_->pervasive_payloads_index_for_logging);
return false;
}
RecordPervasivePayloadIndex(
"Network.CacheTransparency2.SingleKeyedCacheIsUsed",
request_->pervasive_payloads_index_for_logging);
return true;
}
void HttpCache::Transaction::AddDiskCacheWriteTime(base::TimeDelta elapsed) {
total_disk_cache_write_time_ += elapsed;
}
int HttpCache::Transaction::DoLoop(int result) {
DCHECK_NE(STATE_UNSET, next_state_);
DCHECK_NE(STATE_NONE, next_state_);
DCHECK(!in_do_loop_);
int rv = result;
State state = next_state_;
do {
state = next_state_;
next_state_ = STATE_UNSET;
base::AutoReset<bool> scoped_in_do_loop(&in_do_loop_, true);
switch (state) {
case STATE_GET_BACKEND:
DCHECK_EQ(OK, rv);
rv = DoGetBackend();
break;
case STATE_GET_BACKEND_COMPLETE:
rv = DoGetBackendComplete(rv);
break;
case STATE_INIT_ENTRY:
DCHECK_EQ(OK, rv);
rv = DoInitEntry();
break;
case STATE_OPEN_OR_CREATE_ENTRY:
DCHECK_EQ(OK, rv);
rv = DoOpenOrCreateEntry();
break;
case STATE_OPEN_OR_CREATE_ENTRY_COMPLETE:
rv = DoOpenOrCreateEntryComplete(rv);
break;
case STATE_DOOM_ENTRY:
DCHECK_EQ(OK, rv);
rv = DoDoomEntry();
break;
case STATE_DOOM_ENTRY_COMPLETE:
rv = DoDoomEntryComplete(rv);
break;
case STATE_CREATE_ENTRY:
DCHECK_EQ(OK, rv);
rv = DoCreateEntry();
break;
case STATE_CREATE_ENTRY_COMPLETE:
rv = DoCreateEntryComplete(rv);
break;
case STATE_ADD_TO_ENTRY:
DCHECK_EQ(OK, rv);
rv = DoAddToEntry();
break;
case STATE_ADD_TO_ENTRY_COMPLETE:
rv = DoAddToEntryComplete(rv);
break;
case STATE_DONE_HEADERS_ADD_TO_ENTRY_COMPLETE:
rv = DoDoneHeadersAddToEntryComplete(rv);
break;
case STATE_CACHE_READ_RESPONSE:
DCHECK_EQ(OK, rv);
rv = DoCacheReadResponse();
break;
case STATE_CACHE_READ_RESPONSE_COMPLETE:
rv = DoCacheReadResponseComplete(rv);
break;
case STATE_WRITE_UPDATED_PREFETCH_RESPONSE:
DCHECK_EQ(OK, rv);
rv = DoCacheWriteUpdatedPrefetchResponse(rv);
break;
case STATE_WRITE_UPDATED_PREFETCH_RESPONSE_COMPLETE:
rv = DoCacheWriteUpdatedPrefetchResponseComplete(rv);
break;
case STATE_CACHE_DISPATCH_VALIDATION:
DCHECK_EQ(OK, rv);
rv = DoCacheDispatchValidation();
break;
case STATE_CACHE_QUERY_DATA:
DCHECK_EQ(OK, rv);
rv = DoCacheQueryData();
break;
case STATE_CACHE_QUERY_DATA_COMPLETE:
rv = DoCacheQueryDataComplete(rv);
break;
case STATE_START_PARTIAL_CACHE_VALIDATION:
DCHECK_EQ(OK, rv);
rv = DoStartPartialCacheValidation();
break;
case STATE_COMPLETE_PARTIAL_CACHE_VALIDATION:
rv = DoCompletePartialCacheValidation(rv);
break;
case STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT:
DCHECK_EQ(OK, rv);
rv = DoCacheUpdateStaleWhileRevalidateTimeout();
break;
case STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT_COMPLETE:
rv = DoCacheUpdateStaleWhileRevalidateTimeoutComplete(rv);
break;
case STATE_CONNECTED_CALLBACK:
rv = DoConnectedCallback();
break;
case STATE_CONNECTED_CALLBACK_COMPLETE:
rv = DoConnectedCallbackComplete(rv);
break;
case STATE_SETUP_ENTRY_FOR_READ:
DCHECK_EQ(OK, rv);
rv = DoSetupEntryForRead();
break;
case STATE_SEND_REQUEST:
DCHECK_EQ(OK, rv);
rv = DoSendRequest();
break;
case STATE_SEND_REQUEST_COMPLETE:
rv = DoSendRequestComplete(rv);
break;
case STATE_SUCCESSFUL_SEND_REQUEST:
DCHECK_EQ(OK, rv);
rv = DoSuccessfulSendRequest();
break;
case STATE_UPDATE_CACHED_RESPONSE:
DCHECK_EQ(OK, rv);
rv = DoUpdateCachedResponse();
break;
case STATE_CACHE_WRITE_UPDATED_RESPONSE:
DCHECK_EQ(OK, rv);
rv = DoCacheWriteUpdatedResponse();
break;
case STATE_CACHE_WRITE_UPDATED_RESPONSE_COMPLETE:
rv = DoCacheWriteUpdatedResponseComplete(rv);
break;
case STATE_UPDATE_CACHED_RESPONSE_COMPLETE:
rv = DoUpdateCachedResponseComplete(rv);
break;
case STATE_OVERWRITE_CACHED_RESPONSE:
DCHECK_EQ(OK, rv);
rv = DoOverwriteCachedResponse();
break;
case STATE_CACHE_WRITE_RESPONSE:
DCHECK_EQ(OK, rv);
rv = DoCacheWriteResponse();
break;
case STATE_CACHE_WRITE_RESPONSE_COMPLETE:
rv = DoCacheWriteResponseComplete(rv);
break;
case STATE_TRUNCATE_CACHED_DATA:
DCHECK_EQ(OK, rv);
rv = DoTruncateCachedData();
break;
case STATE_TRUNCATE_CACHED_DATA_COMPLETE:
rv = DoTruncateCachedDataComplete(rv);
break;
case STATE_PARTIAL_HEADERS_RECEIVED:
DCHECK_EQ(OK, rv);
rv = DoPartialHeadersReceived();
break;
case STATE_HEADERS_PHASE_CANNOT_PROCEED:
rv = DoHeadersPhaseCannotProceed(rv);
break;
case STATE_FINISH_HEADERS:
rv = DoFinishHeaders(rv);
break;
case STATE_FINISH_HEADERS_COMPLETE:
rv = DoFinishHeadersComplete(rv);
break;
case STATE_NETWORK_READ_CACHE_WRITE:
DCHECK_EQ(OK, rv);
rv = DoNetworkReadCacheWrite();
break;
case STATE_NETWORK_READ_CACHE_WRITE_COMPLETE:
rv = DoNetworkReadCacheWriteComplete(rv);
break;
case STATE_CACHE_READ_DATA:
DCHECK_EQ(OK, rv);
rv = DoCacheReadData();
break;
case STATE_CACHE_READ_DATA_COMPLETE:
rv = DoCacheReadDataComplete(rv);
break;
case STATE_NETWORK_READ:
DCHECK_EQ(OK, rv);
rv = DoNetworkRead();
break;
case STATE_NETWORK_READ_COMPLETE:
rv = DoNetworkReadComplete(rv);
break;
case STATE_MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE:
DCHECK_EQ(0, rv);
rv = DoMarkSingleKeyedCacheEntryUnusable();
break;
case STATE_MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE_COMPLETE:
rv = DoMarkSingleKeyedCacheEntryUnusableComplete(rv);
break;
default:
NOTREACHED() << "bad state " << state;
rv = ERR_FAILED;
break;
}
DCHECK(next_state_ != STATE_UNSET) << "Previous state was " << state;
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
DCHECK(reading_ || rv != OK || !entry_ ||
state == STATE_FINISH_HEADERS_COMPLETE);
if (rv != ERR_IO_PENDING && !callback_.is_null()) {
read_buf_ = nullptr;
std::move(callback_).Run(rv);
}
return rv;
}
int HttpCache::Transaction::DoGetBackend() {
cache_pending_ = true;
TransitionToState(STATE_GET_BACKEND_COMPLETE);
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_GET_BACKEND);
return cache_->GetBackendForTransaction(this);
}
int HttpCache::Transaction::DoGetBackendComplete(int result) {
DCHECK(result == OK || result == ERR_FAILED);
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_GET_BACKEND,
result);
cache_pending_ = false;
mode_ = NONE;
if (!ShouldPassThrough()) {
cache_key_ =
*cache_->GenerateCacheKeyForRequest(request_, use_single_keyed_cache_);
if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_CACHE_MISS;
}
mode_ = READ;
} else if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
mode_ = WRITE;
} else {
mode_ = READ_WRITE;
}
if (external_validation_.initialized) {
if (mode_ & WRITE) {
mode_ = UPDATE;
} else {
mode_ = NONE;
}
}
}
if ((method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH") &&
mode_ != READ_WRITE && mode_ != WRITE) {
mode_ = NONE;
}
if (method_ == "HEAD" && mode_ == WRITE)
mode_ = NONE;
if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_CACHE_MISS;
}
if (mode_ == NONE) {
if (partial_) {
partial_->RestoreHeaders(&custom_request_->extra_headers);
partial_.reset();
}
TransitionToState(STATE_SEND_REQUEST);
} else {
TransitionToState(STATE_INIT_ENTRY);
}
range_requested_ = (partial_.get() != nullptr);
return OK;
}
int HttpCache::Transaction::DoInitEntry() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoInitEntry",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(!new_entry_);
if (!cache_.get()) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_UNEXPECTED;
}
if (mode_ == WRITE) {
TransitionToState(STATE_DOOM_ENTRY);
return OK;
}
TransitionToState(STATE_OPEN_OR_CREATE_ENTRY);
return OK;
}
int HttpCache::Transaction::DoOpenOrCreateEntry() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoOpenOrCreateEntry",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(!new_entry_);
TransitionToState(STATE_OPEN_OR_CREATE_ENTRY_COMPLETE);
cache_pending_ = true;
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_OPEN_OR_CREATE_ENTRY);
first_cache_access_since_ = TimeTicks::Now();
#if BUILDFLAG(IS_OHOS)
TRACE_EVENT1("navigation", "PAGE_LOAD_TIME",
"requestStart", first_cache_access_since_);
#endif
const bool has_opened_or_created_entry = has_opened_or_created_entry_;
has_opened_or_created_entry_ = true;
record_entry_open_or_creation_time_ = false;
new_entry_ = cache_->FindActiveEntry(cache_key_);
if (new_entry_)
return OK;
uint8_t in_memory_info =
cache_->GetCurrentBackend()->GetEntryInMemoryData(cache_key_);
bool entry_not_suitable = false;
if (MaybeRejectBasedOnEntryInMemoryData(in_memory_info)) {
cache_->GetCurrentBackend()->DoomEntry(cache_key_, priority_,
base::DoNothing());
entry_not_suitable = true;
DCHECK_EQ(mode_, READ_WRITE);
couldnt_conditionalize_request_ = true;
validation_cause_ = VALIDATION_CAUSE_ZERO_FRESHNESS;
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE);
}
if (!has_opened_or_created_entry) {
record_entry_open_or_creation_time_ = true;
}
if (mode_ != READ_WRITE || ShouldOpenOnlyMethods()) {
if (entry_not_suitable) {
return net::ERR_CACHE_ENTRY_NOT_SUITABLE;
}
return cache_->OpenEntry(cache_key_, &new_entry_, this);
}
return cache_->OpenOrCreateEntry(cache_key_, &new_entry_, this);
}
int HttpCache::Transaction::DoOpenOrCreateEntryComplete(int result) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::DoOpenOrCreateEntryComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result",
(result == OK ? (new_entry_->opened ? "opened" : "created") : "failed"));
const bool record_uma =
record_entry_open_or_creation_time_ && cache_ &&
cache_->GetCurrentBackend() &&
cache_->GetCurrentBackend()->GetCacheType() != MEMORY_CACHE;
record_entry_open_or_creation_time_ = false;
net_log_.EndEvent(NetLogEventType::HTTP_CACHE_OPEN_OR_CREATE_ENTRY, [&] {
base::Value::Dict params;
if (result == OK) {
params.Set("result", new_entry_->opened ? "opened" : "created");
} else {
params.Set("net_error", result);
}
return params;
});
cache_pending_ = false;
if (result == OK) {
if (new_entry_->opened) {
if (record_uma) {
base::UmaHistogramTimes(
"HttpCache.OpenDiskEntry",
base::TimeTicks::Now() - first_cache_access_since_);
}
} else {
if (record_uma) {
base::UmaHistogramTimes(
"HttpCache.CreateDiskEntry",
base::TimeTicks::Now() - first_cache_access_since_);
}
mode_ = WRITE;
}
TransitionToState(STATE_ADD_TO_ENTRY);
return OK;
}
if (result == ERR_CACHE_RACE) {
TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
return OK;
}
DLOG(WARNING) << "Unable to open or create cache entry";
if (ShouldOpenOnlyMethods()) {
mode_ = NONE;
TransitionToState(STATE_SEND_REQUEST);
return OK;
}
switch (mode_) {
case READ:
TransitionToState(STATE_FINISH_HEADERS);
return ERR_CACHE_MISS;
case READ_WRITE:
mode_ = NONE;
if (partial_)
partial_->RestoreHeaders(&custom_request_->extra_headers);
TransitionToState(STATE_SEND_REQUEST);
break;
case UPDATE:
DCHECK(!partial_);
mode_ = NONE;
TransitionToState(STATE_SEND_REQUEST);
break;
default:
NOTREACHED();
}
return OK;
}
int HttpCache::Transaction::DoDoomEntry() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoDoomEntry",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
TransitionToState(STATE_DOOM_ENTRY_COMPLETE);
cache_pending_ = true;
if (first_cache_access_since_.is_null())
first_cache_access_since_ = TimeTicks::Now();
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_DOOM_ENTRY);
return cache_->DoomEntry(cache_key_, this);
}
int HttpCache::Transaction::DoDoomEntryComplete(int result) {
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::DoDoomEntryComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", result);
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_DOOM_ENTRY,
result);
cache_pending_ = false;
TransitionToState(result == ERR_CACHE_RACE
? STATE_HEADERS_PHASE_CANNOT_PROCEED
: STATE_CREATE_ENTRY);
return OK;
}
int HttpCache::Transaction::DoCreateEntry() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoCreateEntry",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(!new_entry_);
TransitionToState(STATE_CREATE_ENTRY_COMPLETE);
cache_pending_ = true;
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_CREATE_ENTRY);
return cache_->CreateEntry(cache_key_, &new_entry_, this);
}
int HttpCache::Transaction::DoCreateEntryComplete(int result) {
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::DoCreateEntryComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", result);
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_CREATE_ENTRY,
result);
cache_pending_ = false;
switch (result) {
case OK:
TransitionToState(STATE_ADD_TO_ENTRY);
break;
case ERR_CACHE_RACE:
TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
break;
default:
DLOG(WARNING) << "Unable to create cache entry";
mode_ = NONE;
if (!done_headers_create_new_entry_) {
if (partial_)
partial_->RestoreHeaders(&custom_request_->extra_headers);
TransitionToState(STATE_SEND_REQUEST);
return OK;
}
done_headers_create_new_entry_ = false;
TransitionToState(STATE_CACHE_WRITE_RESPONSE);
}
return OK;
}
int HttpCache::Transaction::DoAddToEntry() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoAddToEntry",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(new_entry_);
cache_pending_ = true;
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY);
DCHECK(entry_lock_waiting_since_.is_null());
new_entry_->opened = true;
int rv = cache_->AddTransactionToEntry(new_entry_, this);
DCHECK_EQ(rv, ERR_IO_PENDING);
if (done_headers_create_new_entry_) {
DCHECK_EQ(mode_, WRITE);
TransitionToState(STATE_DONE_HEADERS_ADD_TO_ENTRY_COMPLETE);
return rv;
}
TransitionToState(STATE_ADD_TO_ENTRY_COMPLETE);
entry_lock_waiting_since_ = TimeTicks::Now();
AddCacheLockTimeoutHandler(new_entry_);
return rv;
}
void HttpCache::Transaction::AddCacheLockTimeoutHandler(ActiveEntry* entry) {
DCHECK(next_state_ == STATE_ADD_TO_ENTRY_COMPLETE ||
next_state_ == STATE_FINISH_HEADERS_COMPLETE);
if ((bypass_lock_for_test_ && next_state_ == STATE_ADD_TO_ENTRY_COMPLETE) ||
(bypass_lock_after_headers_for_test_ &&
next_state_ == STATE_FINISH_HEADERS_COMPLETE)) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&HttpCache::Transaction::OnCacheLockTimeout,
weak_factory_.GetWeakPtr(), entry_lock_waiting_since_));
} else {
int timeout_milliseconds = 20 * 1000;
if (partial_ && entry->writers && !entry->writers->IsEmpty() &&
entry->writers->IsExclusive()) {
timeout_milliseconds = 25;
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&HttpCache::Transaction::OnCacheLockTimeout,
weak_factory_.GetWeakPtr(), entry_lock_waiting_since_),
base::Milliseconds(timeout_milliseconds));
}
}
int HttpCache::Transaction::DoAddToEntryComplete(int result) {
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::DoAddToEntryComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", result);
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY,
result);
if (cache_ && cache_->GetCurrentBackend() &&
cache_->GetCurrentBackend()->GetCacheType() != MEMORY_CACHE) {
const base::TimeDelta entry_lock_wait =
TimeTicks::Now() - entry_lock_waiting_since_;
base::UmaHistogramTimes("HttpCache.AddTransactionToEntry", entry_lock_wait);
}
entry_lock_waiting_since_ = TimeTicks();
DCHECK(new_entry_);
cache_pending_ = false;
if (result == OK)
entry_ = new_entry_;
new_entry_ = nullptr;
if (result == ERR_CACHE_RACE) {
TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
return OK;
}
if (result == ERR_CACHE_LOCK_TIMEOUT) {
if (mode_ == READ) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_CACHE_MISS;
}
mode_ = NONE;
TransitionToState(STATE_SEND_REQUEST);
if (partial_) {
partial_->RestoreHeaders(&custom_request_->extra_headers);
partial_.reset();
}
return OK;
}
if (!cache_->IsWritingInProgress(entry()))
open_entry_last_used_ = entry_->GetEntry()->GetLastUsed();
if (result != OK) {
NOTREACHED();
TransitionToState(STATE_FINISH_HEADERS);
return result;
}
if (mode_ == WRITE) {
if (partial_)
partial_->RestoreHeaders(&custom_request_->extra_headers);
TransitionToState(STATE_SEND_REQUEST);
} else {
DCHECK(mode_ & READ_META);
TransitionToState(STATE_CACHE_READ_RESPONSE);
}
return OK;
}
int HttpCache::Transaction::DoDoneHeadersAddToEntryComplete(int result) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::DoDoneHeadersAddToEntryComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result);
DCHECK_EQ(result, OK);
DCHECK_EQ(mode_, WRITE);
DCHECK(new_entry_);
DCHECK(response_.headers);
cache_pending_ = false;
done_headers_create_new_entry_ = false;
if (result == ERR_CACHE_RACE) {
TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
return OK;
}
entry_ = new_entry_;
DCHECK_NE(response_.headers->response_code(), net::HTTP_NOT_MODIFIED);
DCHECK(cache_->CanTransactionWriteResponseHeaders(
entry_, this, partial_ != nullptr, false));
TransitionToState(STATE_CACHE_WRITE_RESPONSE);
return OK;
}
int HttpCache::Transaction::DoCacheReadResponse() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoCacheReadResponse",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(entry_);
TransitionToState(STATE_CACHE_READ_RESPONSE_COMPLETE);
io_buf_len_ = entry_->GetEntry()->GetDataSize(kResponseInfoIndex);
read_buf_ = base::MakeRefCounted<IOBuffer>(io_buf_len_);
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_READ_INFO);
BeginDiskCacheAccessTimeCount();
return entry_->GetEntry()->ReadData(kResponseInfoIndex, 0, read_buf_.get(),
io_buf_len_, io_callback_);
}
int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
TRACE_EVENT_WITH_FLOW2("net",
"HttpCacheTransaction::DoCacheReadResponseComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", result, "io_buf_len", io_buf_len_);
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_READ_INFO,
result);
EndDiskCacheAccessTimeCount(DiskCacheAccessType::kRead);
read_headers_since_ = TimeTicks::Now();
#if BUILDFLAG(IS_OHOS)
TRACE_EVENT1("navigation", "PAGE_LOAD_TIME",
"responseStart", read_headers_since_);
#endif
if (result != io_buf_len_ ||
!HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_, &response_,
&truncated_)) {
return OnCacheReadError(result, true);
}
if (ShouldByPassCacheForFirstPartySets(initial_request_->fps_cache_filter,
response_.browser_run_id)) {
result = ERR_CACHE_ENTRY_NOT_SUITABLE;
return OnCacheReadError(result, true);
}
if (response_.single_keyed_cache_entry_unusable) {
RecordPervasivePayloadIndex("Network.CacheTransparency2.MarkedUnusable",
request_->pervasive_payloads_index_for_logging);
if (use_single_keyed_cache_) {
DCHECK(!network_trans_);
use_single_keyed_cache_ = false;
DoneWithEntryForRestartWithCache();
TransitionToState(STATE_GET_BACKEND);
return OK;
} else {
LOG(WARNING) << "Unusable flag set on non-single-keyed cache entry; "
<< "possible disk corruption? (cache key: " << cache_key_
<< ")";
}
}
if (!cache_->IsWritingInProgress(entry())) {
int current_size = entry_->GetEntry()->GetDataSize(kResponseContentIndex);
int64_t full_response_length = response_.headers->GetContentLength();
if (full_response_length == current_size)
truncated_ = false;
if ((truncated_ ||
response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT) &&
!range_requested_ &&
full_response_length > std::numeric_limits<int32_t>::max()) {
DCHECK(!partial_);
DoneWithEntry(false);
TransitionToState(STATE_SEND_REQUEST);
return OK;
}
}
if (response_.restricted_prefetch &&
!(request_->load_flags & LOAD_CAN_USE_RESTRICTED_PREFETCH)) {
TransitionToState(STATE_SEND_REQUEST);
return OK;
}
bool restricted_prefetch_reuse =
response_.restricted_prefetch &&
request_->load_flags & LOAD_CAN_USE_RESTRICTED_PREFETCH;
DCHECK(!restricted_prefetch_reuse || response_.unused_since_prefetch);
if (response_.unused_since_prefetch !=
!!(request_->load_flags & LOAD_PREFETCH)) {
DCHECK(!updated_prefetch_response_);
updated_prefetch_response_ = std::make_unique<HttpResponseInfo>(response_);
updated_prefetch_response_->unused_since_prefetch =
!response_.unused_since_prefetch;
if (response_.restricted_prefetch &&
request_->load_flags & LOAD_CAN_USE_RESTRICTED_PREFETCH) {
updated_prefetch_response_->restricted_prefetch = false;
}
base::UmaHistogramEnumeration("HttpCache.RestrictedPrefetchReuse",
restricted_prefetch_reuse
? RestrictedPrefetchReused::kReused
: RestrictedPrefetchReused::kNotReused);
TransitionToState(STATE_WRITE_UPDATED_PREFETCH_RESPONSE);
return OK;
}
TransitionToState(STATE_CACHE_DISPATCH_VALIDATION);
return OK;
}
int HttpCache::Transaction::DoCacheWriteUpdatedPrefetchResponse(int result) {
TRACE_EVENT_WITH_FLOW0(
"net", "HttpCacheTransaction::DoCacheWriteUpdatedPrefetchResponse",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(updated_prefetch_response_);
TransitionToState(STATE_WRITE_UPDATED_PREFETCH_RESPONSE_COMPLETE);
return WriteResponseInfoToEntry(*updated_prefetch_response_.get(),
truncated_);
}
int HttpCache::Transaction::DoCacheWriteUpdatedPrefetchResponseComplete(
int result) {
TRACE_EVENT_WITH_FLOW0(
"net",
"HttpCacheTransaction::DoCacheWriteUpdatedPrefetchResponseComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
updated_prefetch_response_.reset();
TransitionToState(STATE_CACHE_DISPATCH_VALIDATION);
return OnWriteResponseInfoToEntryComplete(result);
}
int HttpCache::Transaction::DoCacheDispatchValidation() {
TRACE_EVENT_WITH_FLOW0("net",
"HttpCacheTransaction::DoCacheDispatchValidation",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
if (!entry_) {
TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
return OK;
}
int result = ERR_FAILED;
switch (mode_) {
case READ:
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED);
result = BeginCacheRead();
break;
case READ_WRITE:
result = BeginPartialCacheValidation();
break;
case UPDATE:
result = BeginExternallyConditionalizedRequest();
break;
case WRITE:
default:
NOTREACHED();
}
return result;
}
int HttpCache::Transaction::DoCacheQueryData() {
TransitionToState(STATE_CACHE_QUERY_DATA_COMPLETE);
return entry_->GetEntry()->ReadyForSparseIO(io_callback_);
}
int HttpCache::Transaction::DoCacheQueryDataComplete(int result) {
DCHECK_EQ(OK, result);
if (!cache_.get()) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_UNEXPECTED;
}
return ValidateEntryHeadersAndContinue();
}
int HttpCache::Transaction::DoStartPartialCacheValidation() {
if (mode_ == NONE) {
TransitionToState(STATE_FINISH_HEADERS);
return OK;
}
TransitionToState(STATE_COMPLETE_PARTIAL_CACHE_VALIDATION);
return partial_->ShouldValidateCache(entry_->GetEntry(), io_callback_);
}
int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) {
if (!result && reading_) {
DoneWithEntry(true);
TransitionToState(STATE_FINISH_HEADERS);
return result;
}
if (result < 0) {
TransitionToState(STATE_FINISH_HEADERS);
return result;
}
partial_->PrepareCacheValidation(entry_->GetEntry(),
&custom_request_->extra_headers);
if (reading_ && partial_->IsCurrentRangeCached()) {
TransitionToState(STATE_CONNECTED_CALLBACK);
return OK;
}
return BeginCacheValidation();
}
int HttpCache::Transaction::DoCacheUpdateStaleWhileRevalidateTimeout() {
TRACE_EVENT_WITH_FLOW0(
"net", "HttpCacheTransaction::DoCacheUpdateStaleWhileRevalidateTimeout",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
response_.stale_revalidate_timeout =
cache_->clock_->Now() + kStaleRevalidateTimeout;
TransitionToState(STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT_COMPLETE);
DCHECK(!truncated_);
return WriteResponseInfoToEntry(response_, false);
}
int HttpCache::Transaction::DoCacheUpdateStaleWhileRevalidateTimeoutComplete(
int result) {
TRACE_EVENT_WITH_FLOW0(
"net",
"HttpCacheTransaction::DoCacheUpdateStaleWhileRevalidateTimeoutComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(!reading_);
TransitionToState(STATE_CONNECTED_CALLBACK);
return OnWriteResponseInfoToEntryComplete(result);
}
int HttpCache::Transaction::DoSendRequest() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoSendRequest",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(mode_ & WRITE || mode_ == NONE);
DCHECK(!network_trans_.get());
send_request_since_ = TimeTicks::Now();
int rv =
cache_->network_layer_->CreateTransaction(priority_, &network_trans_);
if (rv != OK) {
TransitionToState(STATE_FINISH_HEADERS);
return rv;
}
network_trans_->SetBeforeNetworkStartCallback(
std::move(before_network_start_callback_));
network_trans_->SetConnectedCallback(connected_callback_);
network_trans_->SetRequestHeadersCallback(request_headers_callback_);
network_trans_->SetEarlyResponseHeadersCallback(
early_response_headers_callback_);
network_trans_->SetResponseHeadersCallback(response_headers_callback_);
network_transaction_info_.old_network_trans_load_timing.reset();
network_transaction_info_.old_remote_endpoint = IPEndPoint();
if (websocket_handshake_stream_base_create_helper_)
network_trans_->SetWebSocketHandshakeStreamCreateHelper(
websocket_handshake_stream_base_create_helper_);
TransitionToState(STATE_SEND_REQUEST_COMPLETE);
rv = network_trans_->Start(request_, io_callback_, net_log_);
return rv;
}
int HttpCache::Transaction::DoSendRequestComplete(int result) {
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::DoSendRequestComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", result);
if (!cache_.get()) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_UNEXPECTED;
}
if (couldnt_conditionalize_request_)
mode_ = WRITE;
if (result == OK) {
TransitionToState(STATE_SUCCESSFUL_SEND_REQUEST);
return OK;
}
const HttpResponseInfo* response = network_trans_->GetResponseInfo();
response_.network_accessed = response->network_accessed;
response_.was_fetched_via_proxy = response->was_fetched_via_proxy;
response_.proxy_server = response->proxy_server;
response_.restricted_prefetch = response->restricted_prefetch;
response_.resolve_error_info = response->resolve_error_info;
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
if (IsCertificateError(result)) {
DCHECK(response);
response_.ssl_info = response->ssl_info;
} else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
DCHECK(response);
response_.cert_request_info = response->cert_request_info;
} else if (result == ERR_INCONSISTENT_IP_ADDRESS_SPACE) {
DoomInconsistentEntry();
} else if (response_.was_cached) {
DoneWithEntry(true);
}
TransitionToState(STATE_FINISH_HEADERS);
return result;
}
int HttpCache::Transaction::DoSuccessfulSendRequest() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoSuccessfulSendRequest",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(!new_response_);
const HttpResponseInfo* new_response = network_trans_->GetResponseInfo();
if (new_response->headers->response_code() == net::HTTP_UNAUTHORIZED ||
new_response->headers->response_code() ==
net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
SetAuthResponse(*new_response);
if (!reading_) {
TransitionToState(STATE_FINISH_HEADERS);
return OK;
}
if (IsReadyToRestartForAuth()) {
TransitionToState(STATE_SEND_REQUEST_COMPLETE);
return network_trans_->RestartWithAuth(AuthCredentials(), io_callback_);
}
if (entry_)
DoomPartialEntry(false);
mode_ = NONE;
partial_.reset();
ResetNetworkTransaction();
TransitionToState(STATE_FINISH_HEADERS);
return ERR_CACHE_AUTH_FAILURE_AFTER_READ;
}
#if BUILDFLAG(IS_OHOS_PRPP)
if (request_ && request_->allow_preload_record && preload_info_ &&
!ShouldDisableCaching(*new_response->headers)) {
UpdateCacheInfo(*new_response);
}
#endif
if (use_single_keyed_cache_ &&
!(new_response->headers->response_code() == 200 ||
new_response->headers->response_code() == 304)) {
mark_single_keyed_cache_entry_unusable_ = true;
}
new_response_ = new_response;
if (!ValidatePartialResponse() && !auth_response_.headers.get()) {
net_log_.AddEvent(NetLogEventType::HTTP_CACHE_RE_SEND_PARTIAL_REQUEST);
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
SetResponse(HttpResponseInfo());
ResetNetworkTransaction();
new_response_ = nullptr;
TransitionToState(STATE_SEND_REQUEST);
return OK;
}
if (handling_206_ && mode_ == READ_WRITE && !truncated_ && !is_sparse_) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
DoneWithEntry(false);
}
if (mode_ == WRITE &&
cache_entry_status_ != CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_NOT_IN_CACHE);
}
if (mode_ == WRITE &&
(method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH")) {
if (NonErrorResponse(new_response_->headers->response_code()) &&
(entry_ && !entry_->doomed)) {
int ret = cache_->DoomEntry(cache_key_, nullptr);
DCHECK_EQ(OK, ret);
}
DoneWithEntry(true);
}
if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) && method_ == "POST" &&
NonErrorResponse(new_response_->headers->response_code()) &&
(!HttpCache::IsSplitCacheEnabled() ||
request_->network_isolation_key.IsFullyPopulated())) {
cache_->DoomMainEntryForUrl(request_->url, request_->network_isolation_key,
request_->is_subframe_document_resource);
}
if (new_response_->headers->response_code() ==
net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE &&
(method_ == "GET" || method_ == "POST")) {
SetResponse(*new_response_);
TransitionToState(STATE_FINISH_HEADERS);
return OK;
}
if (mode_ == READ_WRITE || mode_ == UPDATE) {
if (new_response->headers->response_code() == net::HTTP_NOT_MODIFIED ||
handling_206_) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_VALIDATED);
TransitionToState(STATE_UPDATE_CACHED_RESPONSE);
return OK;
}
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_UPDATED);
mode_ = WRITE;
}
TransitionToState(STATE_OVERWRITE_CACHED_RESPONSE);
return OK;
}
int HttpCache::Transaction::DoUpdateCachedResponse() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoUpdateCachedResponse",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
int rv = OK;
response_.headers->Update(*new_response_->headers.get());
response_.stale_revalidate_timeout = base::Time();
response_.response_time = new_response_->response_time;
response_.request_time = new_response_->request_time;
response_.network_accessed = new_response_->network_accessed;
response_.unused_since_prefetch = new_response_->unused_since_prefetch;
response_.restricted_prefetch = new_response_->restricted_prefetch;
response_.ssl_info = new_response_->ssl_info;
response_.dns_aliases = new_response_->dns_aliases;
#if BUILDFLAG(IS_OHOS)
if (new_response_->headers->response_code() == HTTP_NOT_MODIFIED) {
static bool res = OHOS::NWeb::OhosAdapterHelper::GetInstance()
.GetSystemPropertiesInstance()
.GetBoolParameter("web.304CodeCache.enable", true);
if (res) {
LOG(DEBUG) << "HttpCache::Transaction::DoUpdateCachedResponse set "
"response code: HTTP_NOT_MODIFIED";
response_.code_cache_valid = true;
}
}
#endif
if (mark_single_keyed_cache_entry_unusable_) {
response_.single_keyed_cache_entry_unusable = true;
}
response_.vary_data.Init(*request_, *response_.headers);
if (ShouldDisableCaching(*response_.headers)) {
if (!entry_->doomed) {
int ret = cache_->DoomEntry(cache_key_, nullptr);
DCHECK_EQ(OK, ret);
}
TransitionToState(STATE_UPDATE_CACHED_RESPONSE_COMPLETE);
} else {
if (use_single_keyed_cache_) {
DCHECK_EQ(method_, "GET");
ChecksumHeaders();
}
if (!reading_) {
TransitionToState(STATE_CACHE_WRITE_UPDATED_RESPONSE);
rv = OK;
} else {
TransitionToState(STATE_UPDATE_CACHED_RESPONSE_COMPLETE);
}
}
return rv;
}
int HttpCache::Transaction::DoCacheWriteUpdatedResponse() {
TRACE_EVENT_WITH_FLOW0("net",
"HttpCacheTransaction::DoCacheWriteUpdatedResponse",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
TransitionToState(STATE_CACHE_WRITE_UPDATED_RESPONSE_COMPLETE);
return WriteResponseInfoToEntry(response_, false);
}
int HttpCache::Transaction::DoCacheWriteUpdatedResponseComplete(int result) {
TRACE_EVENT_WITH_FLOW0(
"net", "HttpCacheTransaction::DoCacheWriteUpdatedResponseComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
TransitionToState(STATE_UPDATE_CACHED_RESPONSE_COMPLETE);
return OnWriteResponseInfoToEntryComplete(result);
}
int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::DoUpdateCachedResponseComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result);
if (mode_ == UPDATE) {
DCHECK(!handling_206_);
UpdateSecurityHeadersBeforeForwarding();
DoneWithEntry(true);
} else if (entry_ && !handling_206_) {
DCHECK_EQ(READ_WRITE, mode_);
if ((!partial_ && !cache_->IsWritingInProgress(entry_)) ||
(partial_ && partial_->IsLastRange())) {
mode_ = READ;
}
if (network_trans_)
ResetNetworkTransaction();
} else if (entry_ && handling_206_ && truncated_ &&
partial_->initial_validation()) {
if (network_trans_)
ResetNetworkTransaction();
new_response_ = nullptr;
TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
partial_->SetRangeToStartDownload();
return OK;
}
TransitionToState(STATE_OVERWRITE_CACHED_RESPONSE);
return OK;
}
int HttpCache::Transaction::DoOverwriteCachedResponse() {
TRACE_EVENT_WITH_FLOW0("net",
"HttpCacheTransaction::DoOverwriteCachedResponse",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
if (mode_ & READ) {
TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
return OK;
}
if (handling_206_ && partial_)
partial_->FixContentLength(new_response_->headers.get());
SetResponse(*new_response_);
if (use_single_keyed_cache_) {
DCHECK_EQ(method_, "GET");
ChecksumHeaders();
}
if (method_ == "HEAD") {
DoneWithEntry(false);
new_response_ = nullptr;
TransitionToState(STATE_FINISH_HEADERS);
return OK;
}
if (handling_206_ && !CanResume(false)) {
DoneWithEntry(false);
if (partial_)
partial_->FixResponseHeaders(response_.headers.get(), true);
TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
return OK;
}
if (initial_request_->browser_run_id.has_value())
response_.browser_run_id = initial_request_->browser_run_id;
TransitionToState(STATE_CACHE_WRITE_RESPONSE);
return OK;
}
int HttpCache::Transaction::DoCacheWriteResponse() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoCacheWriteResponse",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(response_.headers);
bool is_match = response_.headers->response_code() == net::HTTP_NOT_MODIFIED;
if (entry_ && !cache_->CanTransactionWriteResponseHeaders(
entry_, this, partial_ != nullptr, is_match)) {
done_headers_create_new_entry_ = true;
mode_ = WRITE;
TransitionToState(STATE_INIT_ENTRY);
cache_->DoomEntryValidationNoMatch(entry_);
entry_ = nullptr;
return OK;
}
if (mark_single_keyed_cache_entry_unusable_) {
response_.single_keyed_cache_entry_unusable = true;
}
TransitionToState(STATE_CACHE_WRITE_RESPONSE_COMPLETE);
return WriteResponseInfoToEntry(response_, truncated_);
}
int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::DoCacheWriteResponseComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result);
TransitionToState(STATE_TRUNCATE_CACHED_DATA);
return OnWriteResponseInfoToEntryComplete(result);
}
int HttpCache::Transaction::DoTruncateCachedData() {
TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoTruncateCachedData",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
TransitionToState(STATE_TRUNCATE_CACHED_DATA_COMPLETE);
if (!entry_)
return OK;
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_WRITE_DATA);
BeginDiskCacheAccessTimeCount();
return entry_->GetEntry()->WriteData(kResponseContentIndex, 0,
nullptr, 0,
io_callback_, true);
}
int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::DoTruncateCachedDataComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result);
EndDiskCacheAccessTimeCount(DiskCacheAccessType::kWrite);
if (entry_) {
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_DATA,
result);
}
TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
return OK;
}
int HttpCache::Transaction::DoPartialHeadersReceived() {
new_response_ = nullptr;
if (partial_ && mode_ != NONE && !reading_) {
partial_->FixResponseHeaders(response_.headers.get(), true);
}
TransitionToState(STATE_FINISH_HEADERS);
return OK;
}
int HttpCache::Transaction::DoHeadersPhaseCannotProceed(int result) {
DCHECK(!reading_);
if (network_trans_)
network_trans_.reset();
new_response_ = nullptr;
SetRequest(net_log_);
entry_ = nullptr;
new_entry_ = nullptr;
last_disk_cache_access_start_time_ = TimeTicks();
if (result == ERR_CACHE_LOCK_TIMEOUT)
effective_load_flags_ |= LOAD_DISABLE_CACHE;
TransitionToState(STATE_GET_BACKEND);
return OK;
}
int HttpCache::Transaction::DoFinishHeaders(int result) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::DoFinishHeaders", TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result);
if (!cache_.get() || !entry_ || result != OK) {
TransitionToState(STATE_NONE);
return result;
}
TransitionToState(STATE_FINISH_HEADERS_COMPLETE);
if (auth_response_.headers.get())
return OK;
int rv = cache_->DoneWithResponseHeaders(entry_, this, partial_ != nullptr);
DCHECK(!reading_ || rv == OK) << "Expected OK, but got " << rv;
if (rv == ERR_IO_PENDING) {
DCHECK(entry_lock_waiting_since_.is_null());
entry_lock_waiting_since_ = TimeTicks::Now();
AddCacheLockTimeoutHandler(entry_);
}
return rv;
}
int HttpCache::Transaction::DoFinishHeadersComplete(int rv) {
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::DoFinishHeadersComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", rv);
entry_lock_waiting_since_ = TimeTicks();
if (rv == ERR_CACHE_RACE || rv == ERR_CACHE_LOCK_TIMEOUT) {
TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
return rv;
}
if (network_trans_ && InWriters()) {
entry_->writers->SetNetworkTransaction(this, std::move(network_trans_),
std::move(checksum_));
moved_network_transaction_to_writers_ = true;
}
if (reading_) {
int reading_state_rv = TransitionToReadingState();
DCHECK_EQ(OK, reading_state_rv);
return OK;
}
TransitionToState(STATE_NONE);
return rv;
}
int HttpCache::Transaction::DoNetworkReadCacheWrite() {
TRACE_EVENT_WITH_FLOW2("net", "HttpCacheTransaction::DoNetworkReadCacheWrite",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"read_offset", read_offset_, "read_buf_len",
read_buf_len_);
DCHECK(InWriters());
TransitionToState(STATE_NETWORK_READ_CACHE_WRITE_COMPLETE);
return entry_->writers->Read(read_buf_, read_buf_len_, io_callback_, this);
}
int HttpCache::Transaction::DoNetworkReadCacheWriteComplete(int result) {
TRACE_EVENT_WITH_FLOW1(
"net", "HttpCacheTransaction::DoNetworkReadCacheWriteComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result);
if (!cache_.get()) {
TransitionToState(STATE_NONE);
return ERR_UNEXPECTED;
}
if (result < 0) {
DCHECK_EQ(result, shared_writing_error_);
DCHECK_EQ(NONE, mode_);
DCHECK(!entry_);
TransitionToState(STATE_NONE);
return result;
}
if (partial_) {
return DoPartialNetworkReadCompleted(result);
}
if (result == 0) {
DCHECK_EQ(NONE, mode_);
DCHECK(!entry_);
} else {
read_offset_ += result;
if (checksum_)
checksum_->Update(read_buf_->data(), result);
}
TransitionToState(STATE_NONE);
return result;
}
int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) {
DCHECK(partial_);
if (result != 0 || truncated_ ||
!(partial_->IsLastRange() || mode_ == WRITE)) {
partial_->OnNetworkReadCompleted(result);
if (result == 0) {
if (network_trans_) {
ResetNetworkTransaction();
} else if (InWriters() && entry_->writers->network_transaction()) {
SaveNetworkTransactionInfo(*(entry_->writers->network_transaction()));
entry_->writers->ResetNetworkTransaction();
}
TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
} else {
TransitionToState(STATE_NONE);
}
return result;
}
if (result == 0) {
DoneWithEntry(true);
}
TransitionToState(STATE_NONE);
return result;
}
int HttpCache::Transaction::DoNetworkRead() {
TRACE_EVENT_WITH_FLOW2(
"net", "HttpCacheTransaction::DoNetworkRead", TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "read_offset",
read_offset_, "read_buf_len", read_buf_len_);
TransitionToState(STATE_NETWORK_READ_COMPLETE);
return network_trans_->Read(read_buf_.get(), read_buf_len_, io_callback_);
}
int HttpCache::Transaction::DoNetworkReadComplete(int result) {
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::DoNetworkReadComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", result);
if (!cache_.get()) {
TransitionToState(STATE_NONE);
return ERR_UNEXPECTED;
}
if (partial_)
return DoPartialNetworkReadCompleted(result);
TransitionToState(STATE_NONE);
return result;
}
int HttpCache::Transaction::DoCacheReadData() {
if (entry_) {
DCHECK(InWriters() || entry_->TransactionInReaders(this));
}
#if BUILDFLAG(IS_OHOS)
TRACE_EVENT1("net", "HttpCache::Transaction::DoCacheReadData",
"url", request_->url.spec());
#endif
TRACE_EVENT_WITH_FLOW2(
"net", "HttpCacheTransaction::DoCacheReadData", TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "read_offset",
read_offset_, "read_buf_len", read_buf_len_);
if (method_ == "HEAD") {
TransitionToState(STATE_NONE);
return 0;
}
DCHECK(entry_);
TransitionToState(STATE_CACHE_READ_DATA_COMPLETE);
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_READ_DATA);
if (partial_) {
return partial_->CacheRead(entry_->GetEntry(), read_buf_.get(),
read_buf_len_, io_callback_);
}
BeginDiskCacheAccessTimeCount();
return entry_->GetEntry()->ReadData(kResponseContentIndex, read_offset_,
read_buf_.get(), read_buf_len_,
io_callback_);
}
int HttpCache::Transaction::DoCacheReadDataComplete(int result) {
EndDiskCacheAccessTimeCount(DiskCacheAccessType::kRead);
if (entry_) {
DCHECK(InWriters() || entry_->TransactionInReaders(this));
}
TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::DoCacheReadDataComplete",
TRACE_ID_LOCAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", result);
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_READ_DATA,
result);
if (!cache_.get()) {
TransitionToState(STATE_NONE);
return ERR_UNEXPECTED;
}
if (partial_) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
return DoPartialCacheReadCompleted(result);
}
if (result > 0) {
read_offset_ += result;
if (checksum_)
checksum_->Update(read_buf_->data(), result);
} else if (result == 0) {
if (!FinishAndCheckChecksum()) {
TransitionToState(STATE_MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE);
return result;
}
DoneWithEntry(true);
} else {
return OnCacheReadError(result, false);
}
TransitionToState(STATE_NONE);
return result;
}
int HttpCache::Transaction::DoMarkSingleKeyedCacheEntryUnusable() {
DCHECK(use_single_keyed_cache_);
response_.single_keyed_cache_entry_unusable = true;
TransitionToState(STATE_MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE_COMPLETE);
return WriteResponseInfoToEntry(response_, false);
}
int HttpCache::Transaction::DoMarkSingleKeyedCacheEntryUnusableComplete(
int result) {
DCHECK_NE(result, ERR_IO_PENDING);
TransitionToState(STATE_NONE);
DoneWithEntry(true);
if (result < 0)
return result;
return 0;
}
void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log) {
net_log_ = net_log;
cache_entry_status_ = CacheEntryStatus::ENTRY_UNDEFINED;
external_validation_.Reset();
range_requested_ = false;
partial_.reset();
request_ = initial_request_;
custom_request_.reset();
effective_load_flags_ = request_->load_flags;
method_ = request_->method;
if (!request_->checksum.empty())
use_single_keyed_cache_ = true;
if (cache_->mode() == DISABLE)
effective_load_flags_ |= LOAD_DISABLE_CACHE;
static const struct {
RAW_PTR_EXCLUSION const HeaderNameAndValue* search;
int load_flag;
} kSpecialHeaders[] = {
{ kPassThroughHeaders, LOAD_DISABLE_CACHE },
{ kForceFetchHeaders, LOAD_BYPASS_CACHE },
{ kForceValidateHeaders, LOAD_VALIDATE_CACHE },
};
bool range_found = false;
bool external_validation_error = false;
bool special_headers = false;
if (request_->extra_headers.HasHeader(HttpRequestHeaders::kRange))
range_found = true;
for (const auto& special_header : kSpecialHeaders) {
if (HeaderMatches(request_->extra_headers, special_header.search)) {
effective_load_flags_ |= special_header.load_flag;
special_headers = true;
break;
}
}
for (size_t i = 0; i < std::size(kValidationHeaders); ++i) {
const ValidationHeaderInfo& info = kValidationHeaders[i];
std::string validation_value;
if (request_->extra_headers.GetHeader(
info.request_header_name, &validation_value)) {
if (!external_validation_.values[i].empty() ||
validation_value.empty()) {
external_validation_error = true;
}
external_validation_.values[i] = validation_value;
external_validation_.initialized = true;
}
}
if (range_found || special_headers || external_validation_.initialized) {
std::string empty;
NetLogRequestHeaders(net_log_,
NetLogEventType::HTTP_CACHE_CALLER_REQUEST_HEADERS,
empty, &request_->extra_headers);
}
if (range_found && external_validation_.initialized) {
LOG(WARNING) << "Byte ranges AND validation headers found.";
effective_load_flags_ |= LOAD_DISABLE_CACHE;
}
if (external_validation_error) {
LOG(WARNING) << "Multiple or malformed validation headers found.";
effective_load_flags_ |= LOAD_DISABLE_CACHE;
}
if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
partial_ = std::make_unique<PartialData>();
if (method_ == "GET" && partial_->Init(request_->extra_headers)) {
custom_request_ = std::make_unique<HttpRequestInfo>(*request_);
custom_request_->extra_headers.RemoveHeader(HttpRequestHeaders::kRange);
request_ = custom_request_.get();
partial_->SetHeaders(custom_request_->extra_headers);
} else {
VLOG(1) << "Invalid byte range found.";
effective_load_flags_ |= LOAD_DISABLE_CACHE;
partial_.reset(nullptr);
}
}
}
bool HttpCache::Transaction::ShouldPassThrough() {
bool cacheable = true;
if (!cache_->disk_cache_.get()) {
cacheable = false;
} else if (effective_load_flags_ & LOAD_DISABLE_CACHE) {
cacheable = false;
}
else if (HttpCache::IsSplitCacheEnabled() &&
request_->network_isolation_key.IsTransient()) {
cacheable = false;
} else if (method_ == "GET" || method_ == "HEAD") {
} else if (method_ == "POST" && request_->upload_data_stream &&
request_->upload_data_stream->identifier()) {
} else if (method_ == "PUT" && request_->upload_data_stream) {
}
else if (method_ == "DELETE" || method_ == "PATCH") {
} else {
cacheable = false;
}
return !cacheable;
}
int HttpCache::Transaction::BeginCacheRead() {
if (response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT ||
partial_) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_CACHE_MISS;
}
if (truncated_) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_CACHE_MISS;
}
if (RequiresValidation() != VALIDATION_NONE) {
TransitionToState(STATE_FINISH_HEADERS);
return ERR_CACHE_MISS;
}
if (method_ == "HEAD")
FixHeadersForHead();
TransitionToState(STATE_FINISH_HEADERS);
return OK;
}
int HttpCache::Transaction::BeginCacheValidation() {
DCHECK_EQ(mode_, READ_WRITE);
ValidationType required_validation = RequiresValidation();
bool skip_validation = (required_validation == VALIDATION_NONE);
bool needs_stale_while_revalidate_cache_update = false;
#if BUILDFLAG(IS_OHOS_PRPP)
if (request_ && request_->allow_preload_record && preload_info_ && skip_validation) {
UpdateCacheInfo(response_);
}
#endif
if ((effective_load_flags_ & LOAD_SUPPORT_ASYNC_REVALIDATION) &&
required_validation == VALIDATION_ASYNCHRONOUS) {
DCHECK_EQ(request_->method, "GET");
skip_validation = true;
response_.async_revalidation_requested = true;
needs_stale_while_revalidate_cache_update =
response_.stale_revalidate_timeout.is_null();
}
if (method_ == "HEAD" && (truncated_ || response_.headers->response_code() ==
net::HTTP_PARTIAL_CONTENT)) {
DCHECK(!partial_);
if (skip_validation) {
DCHECK(!reading_);
TransitionToState(STATE_CONNECTED_CALLBACK);
return OK;
}
TransitionToState(STATE_SEND_REQUEST);
mode_ = NONE;
return OK;
}
if (truncated_) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
skip_validation = !partial_->initial_validation();
}
bool first_read_of_full_from_partial =
is_sparse_ && !reading_ &&
(partial_ && !partial_->range_requested() && !partial_->IsLastRange());
if (partial_ && (is_sparse_ || truncated_) &&
(!partial_->IsCurrentRangeCached() || invalid_range_ ||
first_read_of_full_from_partial)) {
skip_validation = false;
}
if (skip_validation) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED);
DCHECK(!reading_);
TransitionToState(needs_stale_while_revalidate_cache_update
? STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT
: STATE_CONNECTED_CALLBACK);
return OK;
} else {
if (!ConditionalizeRequest()) {
couldnt_conditionalize_request_ = true;
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE);
if (partial_)
return DoRestartPartialRequest();
DCHECK_NE(net::HTTP_PARTIAL_CONTENT, response_.headers->response_code());
}
TransitionToState(STATE_SEND_REQUEST);
}
return OK;
}
int HttpCache::Transaction::BeginPartialCacheValidation() {
DCHECK_EQ(mode_, READ_WRITE);
if (response_.headers->response_code() != net::HTTP_PARTIAL_CONTENT &&
!partial_ && !truncated_)
return BeginCacheValidation();
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
if (method_ == "HEAD")
return BeginCacheValidation();
if (!range_requested_) {
partial_ = std::make_unique<PartialData>();
partial_->SetHeaders(request_->extra_headers);
if (!custom_request_.get()) {
custom_request_ = std::make_unique<HttpRequestInfo>(*request_);
request_ = custom_request_.get();
}
}
TransitionToState(STATE_CACHE_QUERY_DATA);
return OK;
}
int HttpCache::Transaction::ValidateEntryHeadersAndContinue() {
DCHECK_EQ(mode_, READ_WRITE);
if (!partial_->UpdateFromStoredHeaders(
response_.headers.get(), entry_->GetEntry(), truncated_,
cache_->IsWritingInProgress(entry()))) {
return DoRestartPartialRequest();
}
if (response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT)
is_sparse_ = true;
if (!partial_->IsRequestedRangeOK()) {
invalid_range_ = true;
}
TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
return OK;
}
bool HttpCache::Transaction::
ExternallyConditionalizedValidationHeadersMatchEntry() const {
DCHECK(external_validation_.initialized);
for (size_t i = 0; i < std::size(kValidationHeaders); i++) {
if (external_validation_.values[i].empty())
continue;
std::string validator;
response_.headers->EnumerateHeader(
nullptr, kValidationHeaders[i].related_response_header_name,
&validator);
if (validator != external_validation_.values[i]) {
return false;
}
}
return true;
}
int HttpCache::Transaction::BeginExternallyConditionalizedRequest() {
DCHECK_EQ(UPDATE, mode_);
if (response_.headers->response_code() != net::HTTP_OK || truncated_ ||
!ExternallyConditionalizedValidationHeadersMatchEntry()) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
DoneWithEntry(true);
}
TransitionToState(STATE_SEND_REQUEST);
return OK;
}
int HttpCache::Transaction::RestartNetworkRequest() {
DCHECK(mode_ & WRITE || mode_ == NONE);
DCHECK(network_trans_.get());
DCHECK_EQ(STATE_NONE, next_state_);
next_state_ = STATE_SEND_REQUEST_COMPLETE;
int rv = network_trans_->RestartIgnoringLastError(io_callback_);
if (rv != ERR_IO_PENDING)
return DoLoop(rv);
return rv;
}
int HttpCache::Transaction::RestartNetworkRequestWithCertificate(
scoped_refptr<X509Certificate> client_cert,
scoped_refptr<SSLPrivateKey> client_private_key) {
DCHECK(mode_ & WRITE || mode_ == NONE);
DCHECK(network_trans_.get());
DCHECK_EQ(STATE_NONE, next_state_);
next_state_ = STATE_SEND_REQUEST_COMPLETE;
int rv = network_trans_->RestartWithCertificate(
std::move(client_cert), std::move(client_private_key), io_callback_);
if (rv != ERR_IO_PENDING)
return DoLoop(rv);
return rv;
}
int HttpCache::Transaction::RestartNetworkRequestWithAuth(
const AuthCredentials& credentials) {
DCHECK(mode_ & WRITE || mode_ == NONE);
DCHECK(network_trans_.get());
DCHECK_EQ(STATE_NONE, next_state_);
next_state_ = STATE_SEND_REQUEST_COMPLETE;
int rv = network_trans_->RestartWithAuth(credentials, io_callback_);
if (rv != ERR_IO_PENDING)
return DoLoop(rv);
return rv;
}
enum class PrefetchReuseState : uint8_t {
kNone = 0,
kFirstReuse = 1 << 0,
kReusedWithinTimeWindow = 1 << 1,
kFresh = 0 << 2,
kAlwaysValidate = 1 << 2,
kExpired = 2 << 2,
kStale = 3 << 2,
kBitMaskForAllAttributes = kStale | kReusedWithinTimeWindow | kFirstReuse,
kMaxValue = kBitMaskForAllAttributes
};
namespace {
std::underlying_type<PrefetchReuseState>::type to_underlying(
PrefetchReuseState state) {
DCHECK_LE(PrefetchReuseState::kNone, state);
DCHECK_LE(state, PrefetchReuseState::kMaxValue);
return static_cast<std::underlying_type<PrefetchReuseState>::type>(state);
}
PrefetchReuseState to_reuse_state(
std::underlying_type<PrefetchReuseState>::type value) {
PrefetchReuseState state = static_cast<PrefetchReuseState>(value);
DCHECK_LE(PrefetchReuseState::kNone, state);
DCHECK_LE(state, PrefetchReuseState::kMaxValue);
return state;
}
}
PrefetchReuseState ComputePrefetchReuseState(ValidationType type,
bool first_reuse,
bool reused_within_time_window,
bool validate_flag) {
std::underlying_type<PrefetchReuseState>::type reuse_state =
to_underlying(PrefetchReuseState::kNone);
if (first_reuse)
reuse_state |= to_underlying(PrefetchReuseState::kFirstReuse);
if (reused_within_time_window)
reuse_state |= to_underlying(PrefetchReuseState::kReusedWithinTimeWindow);
if (validate_flag)
reuse_state |= to_underlying(PrefetchReuseState::kAlwaysValidate);
else {
switch (type) {
case VALIDATION_SYNCHRONOUS:
reuse_state |= to_underlying(PrefetchReuseState::kExpired);
break;
case VALIDATION_ASYNCHRONOUS:
reuse_state |= to_underlying(PrefetchReuseState::kStale);
break;
case VALIDATION_NONE:
reuse_state |= to_underlying(PrefetchReuseState::kFresh);
break;
}
}
return to_reuse_state(reuse_state);
}
ValidationType HttpCache::Transaction::RequiresValidation() {
if (!(effective_load_flags_ & LOAD_SKIP_VARY_CHECK) &&
response_.vary_data.is_valid() &&
!response_.vary_data.MatchesRequest(*request_,
*response_.headers.get())) {
vary_mismatch_ = true;
validation_cause_ = VALIDATION_CAUSE_VARY_MISMATCH;
return VALIDATION_SYNCHRONOUS;
}
if (effective_load_flags_ & LOAD_SKIP_CACHE_VALIDATION)
return VALIDATION_NONE;
if (method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH")
return VALIDATION_SYNCHRONOUS;
bool validate_flag = effective_load_flags_ & LOAD_VALIDATE_CACHE;
ValidationType validation_required_by_headers =
validate_flag ? VALIDATION_SYNCHRONOUS
: response_.headers->RequiresValidation(
response_.request_time, response_.response_time,
cache_->clock_->Now());
base::TimeDelta response_time_in_cache =
cache_->clock_->Now() - response_.response_time;
if (!base::FeatureList::IsEnabled(
features::kPrefetchFollowsNormalCacheSemantics) &&
!(effective_load_flags_ & LOAD_PREFETCH) &&
(response_time_in_cache >= base::TimeDelta())) {
bool reused_within_time_window =
response_time_in_cache < base::Minutes(kPrefetchReuseMins);
bool first_reuse = response_.unused_since_prefetch;
base::UmaHistogramLongTimes("HttpCache.PrefetchReuseTime",
response_time_in_cache);
if (first_reuse) {
base::UmaHistogramLongTimes("HttpCache.PrefetchFirstReuseTime",
response_time_in_cache);
}
base::UmaHistogramEnumeration(
"HttpCache.PrefetchReuseState",
ComputePrefetchReuseState(validation_required_by_headers, first_reuse,
reused_within_time_window, validate_flag));
if (first_reuse && reused_within_time_window) {
return VALIDATION_NONE;
}
}
if (validate_flag) {
validation_cause_ = VALIDATION_CAUSE_VALIDATE_FLAG;
return VALIDATION_SYNCHRONOUS;
}
if (validation_required_by_headers != VALIDATION_NONE) {
HttpResponseHeaders::FreshnessLifetimes lifetimes =
response_.headers->GetFreshnessLifetimes(response_.response_time);
if (lifetimes.freshness == base::TimeDelta()) {
validation_cause_ = VALIDATION_CAUSE_ZERO_FRESHNESS;
} else {
validation_cause_ = VALIDATION_CAUSE_STALE;
}
}
if (validation_required_by_headers == VALIDATION_ASYNCHRONOUS) {
if (request_->method != "GET")
return VALIDATION_SYNCHRONOUS;
if (!response_.stale_revalidate_timeout.is_null() &&
response_.stale_revalidate_timeout < cache_->clock_->Now()) {
return VALIDATION_SYNCHRONOUS;
}
}
return validation_required_by_headers;
}
bool HttpCache::Transaction::IsResponseConditionalizable(
std::string* etag_value,
std::string* last_modified_value) const {
DCHECK(response_.headers.get());
if (response_.headers->response_code() != net::HTTP_OK &&
response_.headers->response_code() != net::HTTP_PARTIAL_CONTENT) {
return false;
}
if (response_.headers->GetHttpVersion() >= HttpVersion(1, 1))
response_.headers->EnumerateHeader(nullptr, "etag", etag_value);
response_.headers->EnumerateHeader(nullptr, "last-modified",
last_modified_value);
if (etag_value->empty() && last_modified_value->empty())
return false;
return true;
}
bool HttpCache::Transaction::ShouldOpenOnlyMethods() const {
return method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH" ||
(method_ == "HEAD" && mode_ == READ_WRITE);
}
bool HttpCache::Transaction::ConditionalizeRequest() {
DCHECK(response_.headers.get());
if (method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH")
return false;
if (fail_conditionalization_for_test_)
return false;
std::string etag_value;
std::string last_modified_value;
if (!IsResponseConditionalizable(&etag_value, &last_modified_value))
return false;
DCHECK(response_.headers->response_code() != net::HTTP_PARTIAL_CONTENT ||
response_.headers->HasStrongValidators());
if (vary_mismatch_) {
last_modified_value.clear();
if (etag_value.empty())
return false;
}
if (!partial_) {
custom_request_ = std::make_unique<HttpRequestInfo>(*request_);
request_ = custom_request_.get();
}
DCHECK(custom_request_.get());
bool use_if_range =
partial_ && !partial_->IsCurrentRangeCached() && !invalid_range_;
if (!etag_value.empty()) {
if (use_if_range) {
custom_request_->extra_headers.SetHeader(
HttpRequestHeaders::kIfRange, etag_value);
} else {
custom_request_->extra_headers.SetHeader(
HttpRequestHeaders::kIfNoneMatch, etag_value);
}
if (partial_ && !partial_->IsCurrentRangeCached())
return true;
}
if (!last_modified_value.empty()) {
if (use_if_range) {
custom_request_->extra_headers.SetHeader(
HttpRequestHeaders::kIfRange, last_modified_value);
} else {
custom_request_->extra_headers.SetHeader(
HttpRequestHeaders::kIfModifiedSince, last_modified_value);
}
}
return true;
}
bool HttpCache::Transaction::MaybeRejectBasedOnEntryInMemoryData(
uint8_t in_memory_info) {
if (partial_)
return false;
DCHECK_NE(mode_, WRITE);
if (mode_ != READ_WRITE)
return false;
if (effective_load_flags_ & LOAD_SKIP_CACHE_VALIDATION ||
effective_load_flags_ & LOAD_ONLY_FROM_CACHE)
return false;
return (in_memory_info & HINT_UNUSABLE_PER_CACHING_HEADERS) ==
HINT_UNUSABLE_PER_CACHING_HEADERS;
}
bool HttpCache::Transaction::ComputeUnusablePerCachingHeaders() {
if (response_.unused_since_prefetch)
return false;
std::string etag_ignored, last_modified_ignored;
if (IsResponseConditionalizable(&etag_ignored, &last_modified_ignored))
return false;
return response_.headers->GetFreshnessLifetimes(response_.response_time)
.freshness.is_zero();
}
bool HttpCache::Transaction::ValidatePartialResponse() {
const HttpResponseHeaders* headers = new_response_->headers.get();
int response_code = headers->response_code();
bool partial_response = (response_code == net::HTTP_PARTIAL_CONTENT);
handling_206_ = false;
if (!entry_ || method_ != "GET")
return true;
if (invalid_range_) {
DCHECK(!reading_);
if (partial_response || response_code == net::HTTP_OK) {
DoomPartialEntry(true);
mode_ = NONE;
} else {
if (response_code == net::HTTP_NOT_MODIFIED) {
SetResponse(*new_response_);
partial_->FixResponseHeaders(response_.headers.get(), false);
}
IgnoreRangeRequest();
}
return true;
}
if (!partial_) {
if (partial_response)
IgnoreRangeRequest();
return true;
}
bool failure = response_code == net::HTTP_OK ||
response_code == net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE;
if (partial_->IsCurrentRangeCached()) {
if (partial_response)
failure = true;
if (response_code == net::HTTP_NOT_MODIFIED &&
partial_->ResponseHeadersOK(headers))
return true;
} else {
if (partial_response) {
if (partial_->ResponseHeadersOK(headers)) {
handling_206_ = true;
return true;
} else {
failure = true;
}
}
if (!reading_ && !is_sparse_ && !partial_response) {
if (response_code == net::HTTP_OK ||
(!truncated_ && response_code != net::HTTP_NOT_MODIFIED &&
response_code != net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE)) {
DCHECK((truncated_ && !partial_->IsLastRange()) || range_requested_);
partial_.reset();
truncated_ = false;
return true;
}
}
if (truncated_)
failure = true;
}
if (failure) {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
mode_ = NONE;
if (is_sparse_ || truncated_) {
if (!reading_ && !partial_->IsLastRange()) {
ResetPartialState(true);
return false;
}
LOG(WARNING) << "Failed to revalidate partial entry";
}
DoomPartialEntry(true);
return true;
}
IgnoreRangeRequest();
return true;
}
void HttpCache::Transaction::IgnoreRangeRequest() {
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
DoneWithEntry(mode_ != WRITE);
partial_.reset(nullptr);
}
int HttpCache::Transaction::DoConnectedCallback() {
TransitionToState(STATE_CONNECTED_CALLBACK_COMPLETE);
if (connected_callback_.is_null()) {
return OK;
}
auto type = response_.was_fetched_via_proxy ? TransportType::kCachedFromProxy
: TransportType::kCached;
return connected_callback_.Run(
TransportInfo(type, response_.remote_endpoint, ""), io_callback_);
}
int HttpCache::Transaction::DoConnectedCallbackComplete(int result) {
if (result != OK) {
if (result ==
ERR_CACHED_IP_ADDRESS_SPACE_BLOCKED_BY_LOCAL_NETWORK_ACCESS_POLICY) {
DoomInconsistentEntry();
UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
TransitionToState(reading_ ? STATE_SEND_REQUEST
: STATE_HEADERS_PHASE_CANNOT_PROCEED);
return OK;
}
if (result == ERR_INCONSISTENT_IP_ADDRESS_SPACE) {
DoomInconsistentEntry();
} else {
DoneWithEntry(true);
}
TransitionToState(STATE_NONE);
return result;
}
if (reading_) {
DCHECK(partial_);
TransitionToState(STATE_CACHE_READ_DATA);
} else {
TransitionToState(STATE_SETUP_ENTRY_FOR_READ);
}
return OK;
}
void HttpCache::Transaction::DoomInconsistentEntry() {
cache_->DoomActiveEntry(cache_key_);
DoneWithEntry(false);
}
void HttpCache::Transaction::FixHeadersForHead() {
if (response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT) {
response_.headers->RemoveHeader("Content-Range");
response_.headers->ReplaceStatusLine("HTTP/1.1 200 OK");
}
}
int HttpCache::Transaction::DoSetupEntryForRead() {
if (network_trans_)
ResetNetworkTransaction();
if (!entry_) {
TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
return OK;
}
if (partial_) {
if (truncated_ || is_sparse_ ||
(!invalid_range_ &&
(response_.headers->response_code() == net::HTTP_OK ||
response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT))) {
TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
return OK;
} else {
partial_.reset();
}
}
if (!cache_->IsWritingInProgress(entry_))
mode_ = READ;
if (method_ == "HEAD")
FixHeadersForHead();
TransitionToState(STATE_FINISH_HEADERS);
return OK;
}
int HttpCache::Transaction::WriteResponseInfoToEntry(
const HttpResponseInfo& response,
bool truncated) {
DCHECK(response.headers);
if (!entry_)
return OK;
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_WRITE_INFO);
if (IsCertStatusError(response.ssl_info.cert_status) ||
ShouldDisableCaching(*response.headers)) {
if (partial_)
partial_->FixResponseHeaders(response_.headers.get(), true);
bool stopped = StopCachingImpl(false);
DCHECK(stopped);
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_INFO,
OK);
return OK;
}
if (truncated)
DCHECK_EQ(net::HTTP_OK, response.headers->response_code());
bool skip_transient_headers = true;
auto data = base::MakeRefCounted<PickledIOBuffer>();
response.Persist(data->pickle(), skip_transient_headers, truncated);
data->Done();
io_buf_len_ = data->pickle()->size();
if (!entry_->doomed) {
cache_->GetCurrentBackend()->SetEntryInMemoryData(
cache_key_, ComputeUnusablePerCachingHeaders()
? HINT_UNUSABLE_PER_CACHING_HEADERS
: 0);
}
BeginDiskCacheAccessTimeCount();
return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(),
io_buf_len_, io_callback_, true);
}
int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) {
EndDiskCacheAccessTimeCount(DiskCacheAccessType::kWrite);
if (!entry_)
return OK;
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_INFO,
result);
if (result != io_buf_len_) {
DLOG(ERROR) << "failed to write response info to cache";
DoneWithEntry(false);
}
return OK;
}
bool HttpCache::Transaction::StopCachingImpl(bool success) {
bool stopped = false;
if (InWriters()) {
stopped = entry_->writers->StopCaching(success );
if (stopped)
mode_ = NONE;
} else if (entry_) {
stopped = true;
DoneWithEntry(success );
}
return stopped;
}
void HttpCache::Transaction::DoneWithEntry(bool entry_is_complete) {
if (!entry_)
return;
cache_->DoneWithEntry(entry_, this, entry_is_complete, partial_ != nullptr);
entry_ = nullptr;
mode_ = NONE;
}
void HttpCache::Transaction::DoneWithEntryForRestartWithCache() {
if (!entry_)
return;
cache_->DoneWithEntry(entry_, this, true,
partial_ != nullptr);
entry_ = nullptr;
new_entry_ = nullptr;
}
int HttpCache::Transaction::OnCacheReadError(int result, bool restart) {
DLOG(ERROR) << "ReadData failed: " << result;
const int result_for_histogram = std::max(0, -result);
if (restart) {
base::UmaHistogramSparse("HttpCache.ReadErrorRestartable",
result_for_histogram);
} else {
base::UmaHistogramSparse("HttpCache.ReadErrorNonRestartable",
result_for_histogram);
}
if (cache_.get())
cache_->DoomActiveEntry(cache_key_);
if (restart) {
DCHECK(!reading_);
DCHECK(!network_trans_.get());
cache_->DoneWithEntry(entry_, this, true ,
partial_ != nullptr);
entry_ = nullptr;
is_sparse_ = false;
if (partial_)
partial_->RestoreHeaders(&custom_request_->extra_headers);
partial_.reset();
TransitionToState(STATE_GET_BACKEND);
return OK;
}
TransitionToState(STATE_NONE);
return ERR_CACHE_READ_FAILURE;
}
void HttpCache::Transaction::OnCacheLockTimeout(base::TimeTicks start_time) {
if (entry_lock_waiting_since_ != start_time)
return;
DCHECK(next_state_ == STATE_ADD_TO_ENTRY_COMPLETE ||
next_state_ == STATE_FINISH_HEADERS_COMPLETE);
if (!cache_)
return;
if (next_state_ == STATE_ADD_TO_ENTRY_COMPLETE)
cache_->RemovePendingTransaction(this);
else
DoneWithEntry(false );
OnIOComplete(ERR_CACHE_LOCK_TIMEOUT);
}
void HttpCache::Transaction::DoomPartialEntry(bool delete_object) {
DVLOG(2) << "DoomPartialEntry";
if (entry_ && !entry_->doomed) {
int rv = cache_->DoomEntry(cache_key_, nullptr);
DCHECK_EQ(OK, rv);
}
cache_->DoneWithEntry(entry_, this, false ,
partial_ != nullptr);
entry_ = nullptr;
is_sparse_ = false;
truncated_ = false;
if (delete_object)
partial_.reset(nullptr);
}
int HttpCache::Transaction::DoPartialCacheReadCompleted(int result) {
partial_->OnCacheReadCompleted(result);
if (result == 0 && mode_ == READ_WRITE) {
TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
} else if (result < 0) {
return OnCacheReadError(result, false);
} else {
TransitionToState(STATE_NONE);
}
return result;
}
int HttpCache::Transaction::DoRestartPartialRequest() {
net_log_.AddEvent(NetLogEventType::HTTP_CACHE_RESTART_PARTIAL_REQUEST);
ResetPartialState(!range_requested_);
mode_ = WRITE;
TransitionToState(STATE_CREATE_ENTRY);
return OK;
}
void HttpCache::Transaction::ResetPartialState(bool delete_object) {
partial_->RestoreHeaders(&custom_request_->extra_headers);
DoomPartialEntry(delete_object);
if (!delete_object) {
partial_ = std::make_unique<PartialData>();
custom_request_->extra_headers.RemoveHeader(HttpRequestHeaders::kRange);
if (partial_->Init(initial_request_->extra_headers))
partial_->SetHeaders(custom_request_->extra_headers);
else
partial_.reset();
}
}
void HttpCache::Transaction::ResetNetworkTransaction() {
SaveNetworkTransactionInfo(*network_trans_);
network_trans_.reset();
}
const HttpTransaction* HttpCache::Transaction::network_transaction() const {
if (network_trans_)
return network_trans_.get();
if (InWriters())
return entry_->writers->network_transaction();
return nullptr;
}
const HttpTransaction*
HttpCache::Transaction::GetOwnedOrMovedNetworkTransaction() const {
if (network_trans_)
return network_trans_.get();
if (InWriters() && moved_network_transaction_to_writers_)
return entry_->writers->network_transaction();
return nullptr;
}
HttpTransaction* HttpCache::Transaction::network_transaction() {
return const_cast<HttpTransaction*>(
static_cast<const Transaction*>(this)->network_transaction());
}
bool HttpCache::Transaction::CanResume(bool has_data) {
if (has_data && !entry_->GetEntry()->GetDataSize(kResponseContentIndex))
return false;
if (method_ != "GET")
return false;
if (response_.headers->GetContentLength() <= 0 ||
response_.headers->HasHeaderValue("Accept-Ranges", "none") ||
!response_.headers->HasStrongValidators()) {
return false;
}
return true;
}
void HttpCache::Transaction::SetResponse(const HttpResponseInfo& response) {
response_ = response;
if (response_.headers) {
DCHECK(request_);
response_.vary_data.Init(*request_, *response_.headers);
}
SyncCacheEntryStatusToResponse();
}
void HttpCache::Transaction::SetAuthResponse(
const HttpResponseInfo& auth_response) {
auth_response_ = auth_response;
SyncCacheEntryStatusToResponse();
}
void HttpCache::Transaction::UpdateCacheEntryStatus(
CacheEntryStatus new_cache_entry_status) {
DCHECK_NE(CacheEntryStatus::ENTRY_UNDEFINED, new_cache_entry_status);
if (cache_entry_status_ == CacheEntryStatus::ENTRY_OTHER)
return;
DCHECK(cache_entry_status_ == CacheEntryStatus::ENTRY_UNDEFINED ||
new_cache_entry_status == CacheEntryStatus::ENTRY_OTHER);
cache_entry_status_ = new_cache_entry_status;
SyncCacheEntryStatusToResponse();
}
void HttpCache::Transaction::SyncCacheEntryStatusToResponse() {
if (cache_entry_status_ == CacheEntryStatus::ENTRY_UNDEFINED)
return;
response_.cache_entry_status = cache_entry_status_;
if (auth_response_.headers.get()) {
auth_response_.cache_entry_status = cache_entry_status_;
}
}
void HttpCache::Transaction::RecordHistograms() {
DCHECK(!recorded_histograms_);
recorded_histograms_ = true;
web_fonts_histogram::MaybeRecordCacheStatus(
cache_entry_status_,
HttpCache::GetResourceURLFromHttpCacheKey(cache_key_));
if (CacheEntryStatus::ENTRY_UNDEFINED == cache_entry_status_)
return;
if (!cache_.get() || !cache_->GetCurrentBackend() ||
cache_->GetCurrentBackend()->GetCacheType() != DISK_CACHE ||
cache_->mode() != NORMAL || method_ != "GET") {
return;
}
bool is_third_party = false;
DCHECK(request_);
if (request_->possibly_top_frame_origin) {
is_third_party =
!request_->possibly_top_frame_origin->IsSameOriginWith(request_->url);
}
std::string mime_type;
HttpResponseHeaders* response_headers = GetResponseInfo()->headers.get();
const bool is_no_store = response_headers && response_headers->HasHeaderValue(
"cache-control", "no-store");
if (response_headers && response_headers->GetMimeType(&mime_type)) {
if (mime_type == "text/html" &&
(effective_load_flags_ & LOAD_MAIN_FRAME_DEPRECATED)) {
CACHE_STATUS_HISTOGRAMS(".MainFrameHTML");
IS_NO_STORE_HISTOGRAMS(".MainFrameHTML", is_no_store);
} else if (mime_type == "text/html") {
CACHE_STATUS_HISTOGRAMS(".NonMainFrameHTML");
} else if (mime_type == "text/css") {
if (is_third_party) {
CACHE_STATUS_HISTOGRAMS(".CSSThirdParty");
}
CACHE_STATUS_HISTOGRAMS(".CSS");
} else if (base::StartsWith(mime_type, "image/",
base::CompareCase::SENSITIVE)) {
int64_t content_length = response_headers->GetContentLength();
if (content_length >= 0 && content_length < 100) {
CACHE_STATUS_HISTOGRAMS(".TinyImage");
} else if (content_length >= 100) {
CACHE_STATUS_HISTOGRAMS(".NonTinyImage");
}
CACHE_STATUS_HISTOGRAMS(".Image");
} else if (base::EndsWith(mime_type, "javascript",
base::CompareCase::SENSITIVE) ||
base::EndsWith(mime_type, "ecmascript",
base::CompareCase::SENSITIVE)) {
if (is_third_party) {
CACHE_STATUS_HISTOGRAMS(".JavaScriptThirdParty");
}
CACHE_STATUS_HISTOGRAMS(".JavaScript");
} else if (mime_type.find("font") != std::string::npos) {
if (is_third_party) {
CACHE_STATUS_HISTOGRAMS(".FontThirdParty");
}
CACHE_STATUS_HISTOGRAMS(".Font");
} else if (base::StartsWith(mime_type, "audio/",
base::CompareCase::SENSITIVE)) {
CACHE_STATUS_HISTOGRAMS(".Audio");
} else if (base::StartsWith(mime_type, "video/",
base::CompareCase::SENSITIVE)) {
CACHE_STATUS_HISTOGRAMS(".Video");
}
}
CACHE_STATUS_HISTOGRAMS("");
IS_NO_STORE_HISTOGRAMS("", is_no_store);
if (cache_entry_status_ == CacheEntryStatus::ENTRY_OTHER)
return;
DCHECK(!range_requested_) << "Cache entry status " << cache_entry_status_;
DCHECK(!first_cache_access_since_.is_null());
base::TimeTicks now = base::TimeTicks::Now();
base::TimeDelta total_time = now - first_cache_access_since_;
UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone", total_time);
bool did_send_request = !send_request_since_.is_null();
if (did_send_request) {
DCHECK_NE(cache_entry_status_, CacheEntryStatus::ENTRY_USED);
}
DCHECK(
(did_send_request &&
(cache_entry_status_ == CacheEntryStatus::ENTRY_NOT_IN_CACHE ||
cache_entry_status_ == CacheEntryStatus::ENTRY_VALIDATED ||
cache_entry_status_ == CacheEntryStatus::ENTRY_UPDATED ||
cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE)) ||
(!did_send_request &&
(cache_entry_status_ == CacheEntryStatus::ENTRY_USED ||
cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE)));
if (!did_send_request) {
if (cache_entry_status_ == CacheEntryStatus::ENTRY_USED)
UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time);
return;
}
base::TimeDelta before_send_time =
send_request_since_ - first_cache_access_since_;
UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SentRequest", total_time);
UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend", before_send_time);
switch (cache_entry_status_) {
case CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE: {
UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.CantConditionalize",
before_send_time);
break;
}
case CacheEntryStatus::ENTRY_NOT_IN_CACHE: {
UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.NotCached", before_send_time);
break;
}
case CacheEntryStatus::ENTRY_VALIDATED: {
UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Validated", before_send_time);
break;
}
case CacheEntryStatus::ENTRY_UPDATED: {
UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Updated", before_send_time);
break;
}
default:
DCHECK_EQ(cache_entry_status_, CacheEntryStatus::ENTRY_USED);
break;
}
if (!total_disk_cache_read_time_.is_zero()) {
base::UmaHistogramTimes("HttpCache.TotalDiskCacheTimePerTransaction.Read",
total_disk_cache_read_time_);
}
if (!total_disk_cache_write_time_.is_zero()) {
base::UmaHistogramTimes("HttpCache.TotalDiskCacheTimePerTransaction.Write",
total_disk_cache_write_time_);
}
}
bool HttpCache::Transaction::InWriters() const {
return entry_ && entry_->writers && entry_->writers->HasTransaction(this);
}
HttpCache::Transaction::NetworkTransactionInfo::NetworkTransactionInfo() =
default;
HttpCache::Transaction::NetworkTransactionInfo::~NetworkTransactionInfo() =
default;
void HttpCache::Transaction::SaveNetworkTransactionInfo(
const HttpTransaction& transaction) {
DCHECK(!network_transaction_info_.old_network_trans_load_timing);
LoadTimingInfo load_timing;
if (transaction.GetLoadTimingInfo(&load_timing)) {
network_transaction_info_.old_network_trans_load_timing =
std::make_unique<LoadTimingInfo>(load_timing);
}
network_transaction_info_.total_received_bytes +=
transaction.GetTotalReceivedBytes();
network_transaction_info_.total_sent_bytes += transaction.GetTotalSentBytes();
ConnectionAttempts attempts = transaction.GetConnectionAttempts();
for (const auto& attempt : attempts)
network_transaction_info_.old_connection_attempts.push_back(attempt);
network_transaction_info_.old_remote_endpoint = IPEndPoint();
transaction.GetRemoteEndpoint(&network_transaction_info_.old_remote_endpoint);
}
void HttpCache::Transaction::OnIOComplete(int result) {
DoLoop(result);
}
void HttpCache::Transaction::TransitionToState(State state) {
DCHECK(in_do_loop_);
DCHECK_EQ(STATE_UNSET, next_state_) << "Next state is " << state;
next_state_ = state;
}
bool HttpCache::Transaction::ShouldDisableCaching(
const HttpResponseHeaders& headers) const {
if (headers.HasHeaderValue("cache-control", "no-store")) {
return true;
}
bool disable_caching = false;
if (base::FeatureList::IsEnabled(
features::kTurnOffStreamingMediaCachingAlways) ||
(base::FeatureList::IsEnabled(
features::kTurnOffStreamingMediaCachingOnBattery) &&
IsOnBatteryPower())) {
static constexpr int kMaxContentSize = 4096 * 4;
std::string mime_type;
base::CompareCase insensitive_ascii = base::CompareCase::INSENSITIVE_ASCII;
if (headers.GetContentLength() > kMaxContentSize &&
headers.response_code() != net::HTTP_NOT_MODIFIED &&
headers.GetMimeType(&mime_type) &&
(base::StartsWith(mime_type, "video", insensitive_ascii) ||
base::StartsWith(mime_type, "audio", insensitive_ascii))) {
disable_caching = true;
MediaCacheStatusResponseHistogram(
MediaResponseCacheType::kMediaResponseTransactionCacheDisabled);
} else {
MediaCacheStatusResponseHistogram(
MediaResponseCacheType::kMediaResponseTransactionCacheEnabled);
}
}
return disable_caching;
}
void HttpCache::Transaction::UpdateSecurityHeadersBeforeForwarding() {
std::string stored_corp_header;
response_.headers->GetNormalizedHeader("Cross-Origin-Resource-Policy",
&stored_corp_header);
if (!stored_corp_header.empty()) {
new_response_->headers->SetHeader("Cross-Origin-Resource-Policy",
stored_corp_header);
}
return;
}
void HttpCache::Transaction::ChecksumHeaders() {
DCHECK(use_single_keyed_cache_);
DCHECK(!checksum_);
checksum_ = crypto::SecureHash::Create(crypto::SecureHash::SHA256);
constexpr auto kHeadersToInclude = base::MakeFixedFlatSet<base::StringPiece>({
"access-control-allow-credentials",
"access-control-allow-headers",
"access-control-allow-methods",
"access-control-allow-origin",
"access-control-expose-headers",
"access-control-max-age",
"access-control-request-headers",
"access-control-request-method",
"clear-site-data",
"content-encoding",
"content-security-policy",
"content-type",
"cross-origin-embedder-policy",
"cross-origin-opener-policy",
"cross-origin-resource-policy",
"location",
"sec-websocket-accept",
"sec-websocket-extensions",
"sec-websocket-key",
"sec-websocket-protocol",
"sec-websocket-version",
"upgrade",
"vary",
});
std::vector<std::pair<std::string, std::string>> filtered_headers;
filtered_headers.reserve(16);
{
size_t iter = 0;
std::string name;
std::string value;
while (response_.headers->EnumerateHeaderLines(&iter, &name, &value)) {
std::string lowered_name = base::ToLowerASCII(name);
if (kHeadersToInclude.contains(lowered_name)) {
filtered_headers.emplace_back(lowered_name, value);
}
}
}
std::sort(filtered_headers.begin(), filtered_headers.end());
for (const auto& [name, value] : filtered_headers) {
checksum_->Update(name.data(), name.size());
checksum_->Update(": ", 2);
checksum_->Update(value.data(), value.size());
checksum_->Update("\n", 1);
}
checksum_->Update("\n", 1);
}
bool HttpCache::Transaction::FinishAndCheckChecksum() {
if (!checksum_)
return true;
DCHECK(use_single_keyed_cache_);
return ResponseChecksumMatches(std::move(checksum_));
}
void HttpCache::Transaction::BeginDiskCacheAccessTimeCount() {
DCHECK(last_disk_cache_access_start_time_.is_null());
if (partial_) {
return;
}
last_disk_cache_access_start_time_ = TimeTicks::Now();
}
void HttpCache::Transaction::EndDiskCacheAccessTimeCount(
DiskCacheAccessType type) {
if (last_disk_cache_access_start_time_.is_null()) {
return;
}
base::TimeDelta elapsed =
TimeTicks::Now() - last_disk_cache_access_start_time_;
switch (type) {
case DiskCacheAccessType::kRead:
total_disk_cache_read_time_ += elapsed;
break;
case DiskCacheAccessType::kWrite:
total_disk_cache_write_time_ += elapsed;
break;
}
last_disk_cache_access_start_time_ = TimeTicks();
}
#if BUILDFLAG(IS_OHOS_PRPP)
void HttpCache::Transaction::UpdateCacheInfo(const HttpResponseInfo& response) {
if (preload_info_ == nullptr || response.headers == nullptr) {
return;
}
ohos_prp_preload::PRRequestCacheType cache_type =
ohos_prp_preload::PRRequestCacheType::DISABLE_CACHE;
int64_t freshness_life_times = 0;
base::TimeDelta freshnessLifetimes = response.headers->
GetFreshnessLifetimes(response.response_time).freshness;
if (!freshnessLifetimes.is_zero()) {
freshness_life_times = (response.response_time +
freshnessLifetimes -
response.headers
->GetCurrentAge(response.request_time,
response.response_time,
response.response_time)).ToInternalValue();
cache_type = ohos_prp_preload::PRRequestCacheType::FORCE_CACHE;
} else if (response.headers->HasHeader("etag") ||
response.headers->HasHeader("last-modified")) {
cache_type = ohos_prp_preload::PRRequestCacheType::NEGOTIATION_CACHE;
}
std::string e_tag;
response.headers->EnumerateHeader(nullptr, "etag", &e_tag);
std::string last_modified;
response.headers->EnumerateHeader(nullptr, "last-modified", &last_modified);
preload_info_->set_cache_info(cache_type, freshness_life_times, e_tag, last_modified);
}
#endif
}