1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/http/http_cache_transaction.h"
- #include "build/build_config.h" // For IS_POSIX
- #if BUILDFLAG(IS_POSIX)
- #include <unistd.h>
- #endif
- #include <algorithm>
- #include <memory>
- #include <string>
- #include <utility>
- #include "base/auto_reset.h"
- #include "base/bind.h"
- #include "base/callback_helpers.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/location.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" // For HexEncode.
- #include "base/strings/string_piece.h"
- #include "base/strings/string_util.h" // For EqualsCaseInsensitiveASCII.
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/clock.h"
- #include "base/trace_event/common/trace_event_common.h"
- #include "base/trace_event/trace_event.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/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_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"
- 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++;
- }
- // From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6
- // a "non-error response" is one with a 2xx (Successful) or 3xx
- // (Redirection) status code.
- 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
- };
- // These values are persisted to logs. Entries should not be renumbered and
- // numeric values should never be reused.
- enum class RestrictedPrefetchReused {
- kNotReused = 0,
- kReused = 1,
- kMaxValue = kReused
- };
- void RecordPervasivePayloadIndex(const char* histogram_name, int index) {
- if (index != -1) {
- base::UmaHistogramExactLinear(histogram_name, index, 101);
- }
- }
- } // namespace
- #define CACHE_STATUS_HISTOGRAMS(type) \
- UMA_HISTOGRAM_ENUMERATION("HttpCache.Pattern" type, cache_entry_status_, \
- CacheEntryStatus::ENTRY_MAX)
- struct HeaderNameAndValue {
- const char* name;
- const char* value;
- };
- // If the request includes one of these request headers, then avoid caching
- // to avoid getting confused.
- static const HeaderNameAndValue kPassThroughHeaders[] = {
- {"if-unmodified-since", nullptr}, // causes unexpected 412s
- {"if-match", nullptr}, // causes unexpected 412s
- {"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" },
- };
- // If the request includes one of these request headers, then avoid reusing
- // our cached copy if any.
- static const HeaderNameAndValue kForceFetchHeaders[] = {
- {"cache-control", "no-cache"},
- {"pragma", "no-cache"},
- {nullptr, nullptr}};
- // If the request includes one of these request headers, then force our
- // cached copy (if any) to be revalidated before reusing it.
- 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_EVENT1("net", "HttpCacheTransaction::Transaction", "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_EVENT0("net", "HttpCacheTransaction::~Transaction");
- RecordHistograms();
- // We may have to issue another IO, but we should never invoke the callback_
- // after this point.
- callback_.Reset();
- if (cache_) {
- if (entry_) {
- DoneWithEntry(false /* entry_is_complete */);
- } 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(!callback.is_null());
- TRACE_EVENT_WITH_FLOW1("net", "HttpCacheTransaction::Start",
- TRACE_ID_LOCAL(trace_id_), TRACE_EVENT_FLAG_FLOW_OUT,
- "url", request->url.spec());
- // Ensure that we only have one asynchronous call at a time.
- 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);
- // We have to wait until the backend is initialized so we start the SM.
- next_state_ = STATE_GET_BACKEND;
- int rv = DoLoop(OK);
- // Setting this here allows us to check for the existence of a callback_ to
- // determine if we are still inside Start.
- if (rv == ERR_IO_PENDING)
- callback_ = std::move(callback);
- return rv;
- }
- int HttpCache::Transaction::RestartIgnoringLastError(
- CompletionOnceCallback callback) {
- DCHECK(!callback.is_null());
- // Ensure that we only have one asynchronous call at a time.
- 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());
- // Ensure that we only have one asynchronous call at a time.
- 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());
- // Ensure that we only have one asynchronous call at a time.
- DCHECK(callback_.is_null());
- if (!cache_.get())
- return ERR_UNEXPECTED;
- // Clear the intermediate response since we are going to start over.
- 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);
- // TODO(https://crbug.com/1335423): Change to DCHECK_GT() or remove after bug
- // is fixed.
- CHECK_GT(buf_len, 0);
- DCHECK(!callback.is_null());
- DCHECK(callback_.is_null());
- if (!cache_.get())
- return ERR_UNEXPECTED;
- // If we have an intermediate auth response at this point, then it means the
- // user wishes to read the network response (the error page). If there is a
- // previous response in the cache then we should leave it intact.
- 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_) {
- // This can happen when the request should be handled exclusively by
- // the network layer (skipping the cache entirely using
- // LOAD_DISABLE_CACHE) or there was an error during the headers phase
- // due to which the transaction cannot write to the cache or the consumer
- // is reading the auth response from the network.
- // TODO(http://crbug.com/740947) to get rid of this state in future.
- next_state_ = STATE_NETWORK_READ;
- return OK;
- }
- // If there is no network, and no cache entry, then there is nothing to read
- // from.
- next_state_ = STATE_NONE;
- // An error state should be set for the next read, else this transaction
- // should have been terminated once it reached this state. To assert we
- // could dcheck that shared_writing_error_ is set to a valid error value but
- // in some specific conditions (http://crbug.com/806344) it's possible that
- // the consumer does an extra Read in which case the assert will fail.
- return shared_writing_error_;
- }
- // If entry_ is present, the transaction is either a member of entry_->writers
- // or readers.
- if (!InWriters()) {
- // Since transaction is not a writer and we are in Read(), it must be a
- // reader.
- 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 it's a writer and it is partial then it may need to read from the cache
- // or from the network based on whether network transaction is present or not.
- if (partial_) {
- if (entry_->writers->network_transaction())
- next_state_ = STATE_NETWORK_READ_CACHE_WRITE;
- else
- next_state_ = STATE_CACHE_READ_DATA;
- return OK;
- }
- // Full request.
- // If it's a writer and a full request then it may read from the cache if its
- // offset is behind the current offset else from the network.
- 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() {
- // We really don't know where we are now. Hopefully there is no operation in
- // progress, but nothing really prevents this method to be called after we
- // returned ERR_IO_PENDING. We cannot attempt to truncate the entry at this
- // point because we need the state machine for that (and even if we are really
- // free, that would be an asynchronous operation). In other words, keep the
- // entry how it is (it will be marked as truncated at destruction), and let
- // the next piece of code that executes know that we are now reading directly
- // from the net.
- 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 {
- // Null headers means we encountered an error or haven't a response yet
- 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_;
- }
- // TODO(https://crbug.com/1219402): This should check in `response_`
- return &response_;
- }
- LoadState HttpCache::Transaction::GetLoadState() const {
- // If there's no pending callback, the ball is not in the
- // HttpCache::Transaction's court, whatever else may be going on.
- 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;
- // If the cache entry was opened, return that time.
- load_timing_info->send_start = first_cache_access_since_;
- // This time doesn't make much sense when reading from the cache, so just use
- // the same time as send_start.
- load_timing_info->send_end = first_cache_access_since_;
- // Provide the time immediately before parsing a cached entry.
- 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;
- // TODO(shivanisha). Since this function must be invoked before Start() as
- // per the API header, a network transaction should not exist at that point.
- 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_);
- // Ensure this transaction is waiting for a callback.
- 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);
- // Since the transaction can no longer access the network transaction, save
- // all network related info now.
- if (moved_network_transaction_to_writers_ &&
- entry_->writers->network_transaction()) {
- SaveNetworkTransactionInfo(*(entry_->writers->network_transaction()));
- }
- entry_ = nullptr;
- mode_ = NONE;
- // Transactions in the midst of a Read call through writers will get any error
- // code through the IO callback but for idle transactions/transactions reading
- // from the cache, the error for a future Read must be stored here.
- 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.CacheTransparency.MismatchedChecksums",
- request_->pervasive_payloads_index_for_logging);
- return false;
- }
- RecordPervasivePayloadIndex(
- "Network.CacheTransparency.SingleKeyedCacheIsUsed",
- request_->pervasive_payloads_index_for_logging);
- return true;
- }
- //-----------------------------------------------------------------------------
- // A few common patterns: (Foo* means Foo -> FooComplete)
- //
- // 1. Not-cached entry:
- // Start():
- // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
- // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
- // CacheWriteResponse* -> TruncateCachedData* -> PartialHeadersReceived ->
- // FinishHeaders*
- //
- // Read():
- // NetworkReadCacheWrite*/CacheReadData* (if other writers are also writing to
- // the cache)
- //
- // 2. Cached entry, no validation:
- // Start():
- // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
- // CacheReadResponse* -> CacheDispatchValidation ->
- // BeginPartialCacheValidation() -> BeginCacheValidation() ->
- // ConnectedCallback* -> SetupEntryForRead() -> FinishHeaders*
- //
- // Read():
- // CacheReadData*
- //
- // 3. Cached entry, validation (304):
- // Start():
- // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
- // CacheReadResponse* -> CacheDispatchValidation ->
- // BeginPartialCacheValidation() -> BeginCacheValidation() -> SendRequest* ->
- // SuccessfulSendRequest -> UpdateCachedResponse -> CacheWriteUpdatedResponse*
- // -> UpdateCachedResponseComplete -> OverwriteCachedResponse ->
- // PartialHeadersReceived -> FinishHeaders*
- //
- // Read():
- // CacheReadData*
- //
- // 4. Cached entry, validation and replace (200):
- // Start():
- // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
- // CacheReadResponse* -> CacheDispatchValidation ->
- // BeginPartialCacheValidation() -> BeginCacheValidation() -> SendRequest* ->
- // SuccessfulSendRequest -> OverwriteCachedResponse -> CacheWriteResponse* ->
- // DoTruncateCachedData* -> PartialHeadersReceived -> FinishHeaders*
- //
- // Read():
- // NetworkReadCacheWrite*/CacheReadData* (if other writers are also writing to
- // the cache)
- //
- // 5. Sparse entry, partially cached, byte range request:
- // Start():
- // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
- // CacheReadResponse* -> CacheDispatchValidation ->
- // BeginPartialCacheValidation() -> CacheQueryData* ->
- // ValidateEntryHeadersAndContinue() -> StartPartialCacheValidation ->
- // CompletePartialCacheValidation -> BeginCacheValidation() -> SendRequest* ->
- // SuccessfulSendRequest -> UpdateCachedResponse -> CacheWriteUpdatedResponse*
- // -> UpdateCachedResponseComplete -> OverwriteCachedResponse ->
- // PartialHeadersReceived -> FinishHeaders*
- //
- // Read() 1:
- // NetworkReadCacheWrite*
- //
- // Read() 2:
- // NetworkReadCacheWrite* -> StartPartialCacheValidation ->
- // CompletePartialCacheValidation -> ConnectedCallback* -> CacheReadData*
- //
- // Read() 3:
- // CacheReadData* -> StartPartialCacheValidation ->
- // CompletePartialCacheValidation -> BeginCacheValidation() -> SendRequest* ->
- // SuccessfulSendRequest -> UpdateCachedResponse* -> OverwriteCachedResponse
- // -> PartialHeadersReceived -> NetworkReadCacheWrite*
- //
- // 6. HEAD. Not-cached entry:
- // Pass through. Don't save a HEAD by itself.
- // Start():
- // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> SendRequest*
- //
- // 7. HEAD. Cached entry, no validation:
- // Start():
- // The same flow as for a GET request (example #2)
- //
- // Read():
- // CacheReadData (returns 0)
- //
- // 8. HEAD. Cached entry, validation (304):
- // The request updates the stored headers.
- // Start(): Same as for a GET request (example #3)
- //
- // Read():
- // CacheReadData (returns 0)
- //
- // 9. HEAD. Cached entry, validation and replace (200):
- // Pass through. The request dooms the old entry, as a HEAD won't be stored by
- // itself.
- // Start():
- // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
- // CacheReadResponse* -> CacheDispatchValidation ->
- // BeginPartialCacheValidation() -> BeginCacheValidation() -> SendRequest* ->
- // SuccessfulSendRequest -> OverwriteCachedResponse -> FinishHeaders*
- //
- // 10. HEAD. Sparse entry, partially cached:
- // Serve the request from the cache, as long as it doesn't require
- // revalidation. Ignore missing ranges when deciding to revalidate. If the
- // entry requires revalidation, ignore the whole request and go to full pass
- // through (the result of the HEAD request will NOT update the entry).
- //
- // Start(): Basically the same as example 7, as we never create a partial_
- // object for this request.
- //
- // 11. Prefetch, not-cached entry:
- // The same as example 1. The "unused_since_prefetch" bit is stored as true in
- // UpdateCachedResponse.
- //
- // 12. Prefetch, cached entry:
- // Like examples 2-4, only CacheWriteUpdatedPrefetchResponse* is inserted
- // between CacheReadResponse* and CacheDispatchValidation if the
- // unused_since_prefetch bit is unset.
- //
- // 13. Cached entry less than 5 minutes old, unused_since_prefetch is true:
- // Skip validation, similar to example 2.
- // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* ->
- // CacheReadResponse* -> CacheToggleUnusedSincePrefetch* ->
- // CacheDispatchValidation -> BeginPartialCacheValidation() ->
- // BeginCacheValidation() -> ConnectedCallback* -> SetupEntryForRead() ->
- // FinishHeaders*
- //
- // Read():
- // CacheReadData*
- //
- // 14. Cached entry more than 5 minutes old, unused_since_prefetch is true:
- // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between
- // CacheReadResponse* and CacheDispatchValidation.
- 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); // Here "rv" is a count of bytes.
- 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);
- // Assert Start() state machine's allowed last state in successful cases when
- // caching is happening.
- DCHECK(reading_ || rv != OK || !entry_ ||
- state == STATE_FINISH_HEADERS_COMPLETE);
- if (rv != ERR_IO_PENDING && !callback_.is_null()) {
- read_buf_ = nullptr; // Release the buffer before invoking the callback.
- 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;
- // Reset mode_ that might get set in this function. This is done because this
- // function can be invoked multiple times for a transaction.
- mode_ = NONE;
- if (!ShouldPassThrough()) {
- // The flag `use_single_keyed_cache_` will have been changed back to false
- // if the entry was marked unusable and the transaction was restarted in
- // DoCacheReadResponseComplete(), even though `request_` will still have a
- // checksum. So it needs to be passed explicitly.
- cache_key_ =
- *cache_->GenerateCacheKeyForRequest(request_, use_single_keyed_cache_);
- // Requested cache access mode.
- if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
- if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
- // The client has asked for nonsense.
- TransitionToState(STATE_FINISH_HEADERS);
- return ERR_CACHE_MISS;
- }
- mode_ = READ;
- } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
- mode_ = WRITE;
- } else {
- mode_ = READ_WRITE;
- }
- // Downgrade to UPDATE if the request has been externally conditionalized.
- if (external_validation_.initialized) {
- if (mode_ & WRITE) {
- // Strip off the READ_DATA bit (and maybe add back a READ_META bit
- // in case READ was off).
- mode_ = UPDATE;
- } else {
- mode_ = NONE;
- }
- }
- }
- // Use PUT, DELETE, and PATCH only to invalidate existing stored entries.
- if ((method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH") &&
- mode_ != READ_WRITE && mode_ != WRITE) {
- mode_ = NONE;
- }
- // Note that if mode_ == UPDATE (which is tied to external_validation_), the
- // transaction behaves the same for GET and HEAD requests at this point: if it
- // was not modified, the entry is updated and a response is not returned from
- // the cache. If we receive 200, it doesn't matter if there was a validation
- // header or not.
- if (method_ == "HEAD" && mode_ == WRITE)
- mode_ = NONE;
- // If must use cache, then we must fail. This can happen for back/forward
- // navigations to a page generated via a form post.
- 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);
- }
- // This is only set if we have something to do with the response.
- 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();
- 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;
- // See if we already have something working with this cache key.
- new_entry_ = cache_->FindActiveEntry(cache_key_);
- if (new_entry_)
- return OK;
- // See if we could potentially doom the entry based on hints the backend keeps
- // in memory.
- // Currently only SimpleCache utilizes in memory hints. If an entry is found
- // unsuitable, and thus Doomed, SimpleCache can also optimize the
- // OpenOrCreateEntry() call to reduce the overhead of trying to open an entry
- // we know is doomed.
- 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;
- // Documents the case this applies in
- DCHECK_EQ(mode_, READ_WRITE);
- // Record this as CantConditionalize, but otherwise proceed as we would
- // below --- as we've already dropped the old entry.
- 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;
- }
- // mode_ can be anything but NONE or WRITE at this point (READ, UPDATE, or
- // READ_WRITE).
- // READ, UPDATE, certain READ_WRITEs, and some methods shouldn't create, so
- // try only opening.
- if (mode_ != READ_WRITE || ShouldOpenOnlyMethods()) {
- if (entry_not_suitable) {
- // The entry isn't suitable and we can't create a new one.
- 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;
- // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
- // OK, otherwise the cache will end up with an active entry without any
- // transaction attached.
- net_log_.EndEventWithNetErrorCode(
- NetLogEventType::HTTP_CACHE_OPEN_OR_CREATE_ENTRY, result);
- 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_);
- }
- // Entry was created so mode changes to WRITE.
- mode_ = WRITE;
- }
- TransitionToState(STATE_ADD_TO_ENTRY);
- return OK;
- }
- if (result == ERR_CACHE_RACE) {
- TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
- return OK;
- }
- // No need to explicitly handle ERR_CACHE_ENTRY_NOT_SUITABLE as the
- // ShouldOpenOnlyMethods() check will handle it.
- // We were unable to open or create an entry.
- DLOG(WARNING) << "Unable to open or create cache entry";
- if (ShouldOpenOnlyMethods()) {
- // These methods, on failure, should bypass the cache.
- mode_ = NONE;
- TransitionToState(STATE_SEND_REQUEST);
- return OK;
- }
- // Since the operation failed, what we do next depends on the mode_ which can
- // be the following: READ, READ_WRITE, or UPDATE. Note: mode_ cannot be WRITE
- // or NONE at this point as DoInitEntry() handled those cases.
- switch (mode_) {
- case READ:
- // The entry does not exist, and we are not permitted to create a new
- // entry, so we must fail.
- TransitionToState(STATE_FINISH_HEADERS);
- return ERR_CACHE_MISS;
- case READ_WRITE:
- // Unable to open or create; set the mode to NONE in order to bypass the
- // cache entry and read from the network directly.
- mode_ = NONE;
- if (partial_)
- partial_->RestoreHeaders(&custom_request_->extra_headers);
- TransitionToState(STATE_SEND_REQUEST);
- break;
- case UPDATE:
- // There is no cache entry to update; proceed without caching.
- 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);
- // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
- // OK, otherwise the cache will end up with an active entry without any
- // transaction attached.
- 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";
- // Set the mode to NONE in order to bypass the cache entry and read from
- // the network directly.
- mode_ = NONE;
- if (!done_headers_create_new_entry_) {
- if (partial_)
- partial_->RestoreHeaders(&custom_request_->extra_headers);
- TransitionToState(STATE_SEND_REQUEST);
- return OK;
- }
- // The headers have already been received as a result of validation,
- // triggering the doom of the old entry. So no network request needs to
- // be sent. Note that since mode_ is NONE, the response won't be written
- // to cache. Transition to STATE_CACHE_WRITE_RESPONSE as that's the state
- // the transaction left off on when it tried to create the new entry.
- 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());
- // By this point whether the entry was created or opened is no longer relevant
- // for this transaction. However there may be queued transactions that want to
- // use this entry and from their perspective the entry was opened, so change
- // the flag to reflect that.
- new_entry_->opened = true;
- int rv = cache_->AddTransactionToEntry(new_entry_, this);
- DCHECK_EQ(rv, ERR_IO_PENDING);
- // If headers phase is already done then we are here because of validation not
- // matching and creating a new entry. This transaction should be the
- // first transaction of that new entry and thus it will not have cache lock
- // delays, thus returning early from here.
- 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::ThreadTaskRunnerHandle::Get()->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()) {
- // Even though entry_->writers takes care of allowing multiple writers to
- // simultaneously govern reading from the network and writing to the cache
- // for full requests, partial requests are still blocked by the
- // reader/writer lock.
- // Bypassing the cache after 25 ms of waiting for the cache lock
- // eliminates a long running issue, http://crbug.com/31014, where
- // two of the same media resources could not be played back simultaneously
- // due to one locking the cache entry until the entire video was
- // downloaded.
- // Bypassing the cache is not ideal, as we are now ignoring the cache
- // entirely for all range requests to a resource beyond the first. This
- // is however a much more succinct solution than the alternatives, which
- // would require somewhat significant changes to the http caching logic.
- //
- // Allow some timeout slack for the entry addition to complete in case
- // the writer lock is imminently released; we want to avoid skipping
- // the cache if at all possible. See http://crbug.com/408765
- timeout_milliseconds = 25;
- }
- base::ThreadTaskRunnerHandle::Get()->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_;
- // If there is a failure, the cache should have taken care of 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;
- }
- // The cache is busy, bypass it for this transaction.
- mode_ = NONE;
- TransitionToState(STATE_SEND_REQUEST);
- if (partial_) {
- partial_->RestoreHeaders(&custom_request_->extra_headers);
- partial_.reset();
- }
- return OK;
- }
- // TODO(crbug.com/713354) Access timestamp for histograms only if entry is
- // already written, to avoid data race since cache thread can also access
- // this.
- if (!cache_->IsWritingInProgress(entry()))
- open_entry_last_used_ = entry_->GetEntry()->GetLastUsed();
- // TODO(jkarlin): We should either handle the case or DCHECK.
- 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 {
- // We have to read the headers from the cached entry.
- 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);
- // This transaction's response headers did not match its ActiveEntry so it
- // created a new ActiveEntry (new_entry_) to write to (and doomed the old
- // one). Now that the new entry has been created, start writing the response.
- DCHECK_EQ(result, OK);
- DCHECK_EQ(mode_, WRITE);
- DCHECK(new_entry_);
- DCHECK(response_.headers);
- cache_pending_ = false;
- done_headers_create_new_entry_ = false;
- // It is unclear exactly how this state is reached with an ERR_CACHE_RACE, but
- // this check appears to fix a rare crash. See crbug.com/959194.
- 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);
- 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);
- // Record the time immediately before the cached response is parsed.
- read_headers_since_ = TimeTicks::Now();
- if (result != io_buf_len_ ||
- !HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_, &response_,
- &truncated_)) {
- return OnCacheReadError(result, true);
- }
- if (response_.single_keyed_cache_entry_unusable) {
- RecordPervasivePayloadIndex("Network.CacheTransparency.MarkedUnusable",
- request_->pervasive_payloads_index_for_logging);
- // We've read the single keyed entry and it turned out to be unusable. Let's
- // retry reading from the split cache.
- 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_
- << ")";
- }
- }
- // TODO(crbug.com/713354) Only get data size if there is no other transaction
- // currently writing the response body due to the data race mentioned in the
- // associated bug.
- if (!cache_->IsWritingInProgress(entry())) {
- int current_size = entry_->GetEntry()->GetDataSize(kResponseContentIndex);
- int64_t full_response_length = response_.headers->GetContentLength();
- // Some resources may have slipped in as truncated when they're not.
- if (full_response_length == current_size)
- truncated_ = false;
- // The state machine's handling of StopCaching unfortunately doesn't deal
- // well with resources that are larger than 2GB when there is a truncated or
- // sparse cache entry. While the state machine is reworked to resolve this,
- // the following logic is put in place to defer such requests to the
- // network. The cache should not be storing multi gigabyte resources. See
- // http://crbug.com/89567.
- if ((truncated_ ||
- response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT) &&
- !range_requested_ &&
- full_response_length > std::numeric_limits<int32_t>::max()) {
- DCHECK(!partial_);
- // Doom the entry so that no other transaction gets added to this entry
- // and avoid a race of not being able to check this condition because
- // writing is in progress.
- 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;
- }
- // When a restricted prefetch is reused, we lift its reuse restriction.
- 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)) {
- // Either this is the first use of an entry since it was prefetched XOR
- // this is a prefetch. The value of response.unused_since_prefetch is
- // valid for this transaction but the bit needs to be flipped in storage.
- 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_);
- // TODO(jkarlin): If DoUpdateCachedResponse is also called for this
- // transaction then metadata will be written to cache twice. If prefetching
- // becomes more common, consider combining the writes.
- TransitionToState(STATE_WRITE_UPDATED_PREFETCH_RESPONSE_COMPLETE);
- return WriteResponseInfoToEntry(*updated_prefetch_response_.get(), false);
- }
- 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_) {
- // Entry got destroyed when twiddling unused-since-prefetch bit.
- TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
- return OK;
- }
- // We now have access to the cache entry.
- //
- // o if we are a reader for the transaction, then we can start reading the
- // cache entry.
- //
- // o if we can read or write, then we should check if the cache entry needs
- // to be validated and then issue a network request if needed or just read
- // from the cache if the cache entry is already valid.
- //
- // o if we are set to UPDATE, then we are handling an externally
- // conditionalized request (if-modified-since / if-none-match). We check
- // if the request headers define a validation request.
- //
- 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();
- }
- // We may end up here multiple times for a given request.
- 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) {
- // This is the end of the request.
- 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()) {
- // We're about to read a range of bytes from the cache. Signal it to the
- // consumer through the "connected" callback.
- 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);
- 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();
- // Create a network transaction.
- 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_);
- // Old load timing information, if any, is now obsolete.
- 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 we tried to conditionalize the request and failed, we know
- // we won't be reading from the cache after this point.
- 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;
- // Do not record requests that have network errors or restarts.
- UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
- if (IsCertificateError(result)) {
- // If we get a certificate error, then there is a certificate in ssl_info,
- // so GetResponseInfo() should never return NULL here.
- 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(/*entry_is_complete=*/true);
- }
- TransitionToState(STATE_FINISH_HEADERS);
- return result;
- }
- // We received the response headers and there is no error.
- 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;
- }
- // We initiated a second request the caller doesn't know about. We should be
- // able to authenticate this request because we should have authenticated
- // this URL moments ago.
- if (IsReadyToRestartForAuth()) {
- TransitionToState(STATE_SEND_REQUEST_COMPLETE);
- // In theory we should check to see if there are new cookies, but there
- // is no way to do that from here.
- return network_trans_->RestartWithAuth(AuthCredentials(), io_callback_);
- }
- // We have to perform cleanup at this point so that at least the next
- // request can succeed. We do not retry at this point, because data
- // has been read and we have no way to gather credentials. We would
- // fail again, and potentially loop. This can happen if the credentials
- // expire while chrome is suspended.
- if (entry_)
- DoomPartialEntry(false);
- mode_ = NONE;
- partial_.reset();
- ResetNetworkTransaction();
- TransitionToState(STATE_FINISH_HEADERS);
- return ERR_CACHE_AUTH_FAILURE_AFTER_READ;
- }
- // The single-keyed cache only accepts responses with code 200 or 304.
- // Anything else is considered unusable.
- if (use_single_keyed_cache_ &&
- !(new_response->headers->response_code() == 200 ||
- new_response->headers->response_code() == 304)) {
- // Either the new response will be written back to the cache, in which case
- // it will not be reused due to the flag, or it will not be, in which case
- // it will not be reused anyway.
- mark_single_keyed_cache_entry_unusable_ = true;
- }
- new_response_ = new_response;
- if (!ValidatePartialResponse() && !auth_response_.headers.get()) {
- // Something went wrong with this request and we have to restart it.
- // If we have an authentication response, we are exposed to weird things
- // hapenning if the user cancels the authentication before we receive
- // the new response.
- 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_) {
- // We have stored the full entry, but it changed and the server is
- // sending a range. We have to delete the old entry.
- UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
- DoneWithEntry(false);
- }
- if (mode_ == WRITE &&
- cache_entry_status_ != CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE) {
- UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_NOT_IN_CACHE);
- }
- // Invalidate any cached GET with a successful PUT, DELETE, or PATCH.
- 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);
- }
- // Do not invalidate the entry if the request failed.
- DoneWithEntry(true);
- }
- // Invalidate any cached GET with a successful POST. If the network isolation
- // key isn't populated with the split cache active, there will be nothing to
- // invalidate in the cache.
- 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")) {
- // If there is an active entry it may be destroyed with this transaction.
- SetResponse(*new_response_);
- TransitionToState(STATE_FINISH_HEADERS);
- return OK;
- }
- // Are we expecting a response to a conditional query?
- 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;
- }
- // We received 304 or 206 and we want to update the cached response headers.
- 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;
- // Update the cached response based on the headers and properties of
- // new_response_.
- 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;
- // Be careful never to set single_keyed_cache_entry_unusable back to false
- // from true.
- if (mark_single_keyed_cache_entry_unusable_) {
- response_.single_keyed_cache_entry_unusable = true;
- }
- // If the new response didn't have a vary header, we continue to use the
- // header from the stored response per the effect of headers->Update().
- // Update the data with the new/updated request headers.
- 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 we are already reading, we already updated the headers for this
- // request; doing it again will change Content-Length.
- 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_);
- // We got a "not modified" response and already updated the corresponding
- // cache entry above.
- //
- // By stopping to write to the cache now, we make sure that the 304 rather
- // than the cached 200 response, is what will be returned to the user.
- UpdateSecurityHeadersBeforeForwarding();
- DoneWithEntry(true);
- } else if (entry_ && !handling_206_) {
- DCHECK_EQ(READ_WRITE, mode_);
- if ((!partial_ && !cache_->IsWritingInProgress(entry_)) ||
- (partial_ && partial_->IsLastRange())) {
- mode_ = READ;
- }
- // We no longer need the network transaction, so destroy it.
- if (network_trans_)
- ResetNetworkTransaction();
- } else if (entry_ && handling_206_ && truncated_ &&
- partial_->initial_validation()) {
- // We just finished the validation of a truncated entry, and the server
- // is willing to resume the operation. Now we go back and start serving
- // the first part to the user.
- 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;
- }
- // We change the value of Content-Length for partial content.
- 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") {
- // This response is replacing the cached one.
- DoneWithEntry(false);
- new_response_ = nullptr;
- TransitionToState(STATE_FINISH_HEADERS);
- return OK;
- }
- if (handling_206_ && !CanResume(false)) {
- // There is no point in storing this resource because it will never be used.
- // This may change if we support LOAD_ONLY_FROM_CACHE with sparse entries.
- DoneWithEntry(false);
- if (partial_)
- partial_->FixResponseHeaders(response_.headers.get(), true);
- TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED);
- return OK;
- }
- 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);
- // Invalidate any current entry with a successful response if this transaction
- // cannot write to this entry. This transaction then continues to read from
- // the network without writing to the backend.
- 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;
- // The transaction needs to overwrite this response. Doom the current entry,
- // create a new one (by going to STATE_INIT_ENTRY), and then jump straight
- // to writing out the response, bypassing the headers checks. The mode_ is
- // set to WRITE in order to doom any other existing entries that might exist
- // so that this transaction can go straight to writing a response.
- mode_ = WRITE;
- TransitionToState(STATE_INIT_ENTRY);
- cache_->DoomEntryValidationNoMatch(entry_);
- entry_ = nullptr;
- return OK;
- }
- // Be careful never to set single_keyed_cache_entry_unusable back to false
- // from true.
- 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);
- // Truncate the stream.
- return entry_->GetEntry()->WriteData(kResponseContentIndex, /*offset=*/0,
- /*buf=*/nullptr, /*buf_len=*/0,
- io_callback_, /*truncate=*/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);
- 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_) {
- // We are about to return the headers for a byte-range request to the user,
- // so let's fix them.
- partial_->FixResponseHeaders(response_.headers.get(), true);
- }
- TransitionToState(STATE_FINISH_HEADERS);
- return OK;
- }
- int HttpCache::Transaction::DoHeadersPhaseCannotProceed(int result) {
- // If its the Start state machine and it cannot proceed due to a cache
- // failure, restart this transaction.
- DCHECK(!reading_);
- // Reset before invoking SetRequest() which can reset the request info sent to
- // network transaction.
- if (network_trans_)
- network_trans_.reset();
- new_response_ = nullptr;
- SetRequest(net_log_);
- entry_ = nullptr;
- new_entry_ = nullptr;
- // TODO(https://crbug.com/1219402): This should probably clear `response_`,
- // too, once things are fixed so it's safe to do so.
- // Bypass the cache for timeout scenario.
- 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 it was an auth failure, this transaction should continue to be
- // headers_transaction till consumer takes an action, so no need to do
- // anything now.
- // TODO(crbug.com/740947). See the issue for a suggestion for cleaning the
- // state machine to be able to remove this condition.
- if (auth_response_.headers.get())
- return OK;
- // If the transaction needs to wait because another transaction is still
- // writing the response body, it will return ERR_IO_PENDING now and the
- // io_callback_ will be invoked when the wait is done.
- 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 already reading, that means it is a partial request coming back to the
- // headers phase, continue to the appropriate reading state.
- 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;
- }
- // |result| will be error code in case of network read failure and |this|
- // cannot proceed further, so set entry_ to null. |result| will not be error
- // in case of cache write failure since |this| can continue to read from the
- // network. If response is completed, then also set entry to null.
- if (result < 0) {
- // We should have discovered this error in WriterAboutToBeRemovedFromEntry
- 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_);
- // Go to the next range if nothing returned or return the result.
- // TODO(shivanisha) Simplify this condition if possible. It was introduced
- // in https://codereview.chromium.org/545101
- if (result != 0 || truncated_ ||
- !(partial_->IsLastRange() || mode_ == WRITE)) {
- partial_->OnNetworkReadCompleted(result);
- if (result == 0) {
- // We need to move on to the next range.
- 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;
- }
- // Request completed.
- 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() {
- 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_);
- }
- return entry_->GetEntry()->ReadData(kResponseContentIndex, read_offset_,
- read_buf_.get(), read_buf_len_,
- io_callback_);
- }
- int HttpCache::Transaction::DoCacheReadDataComplete(int result) {
- 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_) {
- // Partial requests are confusing to report in histograms because they may
- // have multiple underlying requests.
- 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) { // End of file.
- 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_, /*truncated=*/false);
- }
- int HttpCache::Transaction::DoMarkSingleKeyedCacheEntryUnusableComplete(
- int result) {
- DCHECK_NE(result, ERR_IO_PENDING);
- TransitionToState(STATE_NONE);
- DoneWithEntry(/*entry_is_complete=*/true);
- if (result < 0)
- return result;
- // Return 0 to indicate that we've finished reading the body.
- return 0;
- }
- //-----------------------------------------------------------------------------
- void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log) {
- net_log_ = net_log;
- // Reset the variables that might get set in this function. This is done
- // because this function can be invoked multiple times for a transaction.
- 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;
- // Some headers imply load flags. The order here is significant.
- //
- // LOAD_DISABLE_CACHE : no cache read or write
- // LOAD_BYPASS_CACHE : no cache read
- // LOAD_VALIDATE_CACHE : no cache read unless validation
- //
- // The former modes trump latter modes, so if we find a matching header we
- // can stop iterating kSpecialHeaders.
- //
- static const struct {
- 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;
- }
- }
- // Check for conditionalization headers which may correspond with a
- // cache validation request.
- 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) {
- // Log the headers before request_ is modified.
- std::string empty;
- NetLogRequestHeaders(net_log_,
- NetLogEventType::HTTP_CACHE_CALLER_REQUEST_HEADERS,
- empty, &request_->extra_headers);
- }
- // We don't support ranges and validation headers.
- if (range_found && external_validation_.initialized) {
- LOG(WARNING) << "Byte ranges AND validation headers found.";
- effective_load_flags_ |= LOAD_DISABLE_CACHE;
- }
- // If there is more than one validation header, we can't treat this request as
- // a cache validation, since we don't know for sure which header the server
- // will give us a response for (and they could be contradictory).
- 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)) {
- // We will be modifying the actual range requested to the server, so
- // let's remove the header here.
- // Note that custom_request_ is a shallow copy so will keep the same
- // pointer to upload data stream as in the original request.
- 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 {
- // The range is invalid or we cannot handle it properly.
- VLOG(1) << "Invalid byte range found.";
- effective_load_flags_ |= LOAD_DISABLE_CACHE;
- partial_.reset(nullptr);
- }
- }
- }
- bool HttpCache::Transaction::ShouldPassThrough() {
- bool cacheable = true;
- // We may have a null disk_cache if there is an error we cannot recover from,
- // like not enough disk space, or sharing violations.
- if (!cache_->disk_cache_.get()) {
- cacheable = false;
- } else if (effective_load_flags_ & LOAD_DISABLE_CACHE) {
- cacheable = false;
- }
- // Prevent resources whose origin is opaque from being cached. Blink's memory
- // cache should take care of reusing resources within the current page load,
- // but otherwise a resource with an opaque top-frame origin won’t be used
- // again. Also, if the request does not have a top frame origin, bypass the
- // cache otherwise resources from different pages could share a cached entry
- // in such cases.
- 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) {
- }
- // DELETE and PATCH requests may result in invalidating the cache, so cannot
- // just pass through.
- else if (method_ == "DELETE" || method_ == "PATCH") {
- } else {
- cacheable = false;
- }
- NetworkIsolationKeyPresent nik_present_enum =
- request_->network_isolation_key.IsFullyPopulated()
- ? NetworkIsolationKeyPresent::kPresent
- : cacheable
- ? NetworkIsolationKeyPresent::kNotPresentCacheableRequest
- : NetworkIsolationKeyPresent::kNotPresentNonCacheableRequest;
- UMA_HISTOGRAM_ENUMERATION("HttpCache.NetworkIsolationKeyPresent2",
- nik_present_enum);
- return !cacheable;
- }
- int HttpCache::Transaction::BeginCacheRead() {
- // We don't support any combination of LOAD_ONLY_FROM_CACHE and byte ranges.
- // TODO(jkarlin): Either handle this case or DCHECK.
- if (response_.headers->response_code() == net::HTTP_PARTIAL_CONTENT ||
- partial_) {
- NOTREACHED();
- TransitionToState(STATE_FINISH_HEADERS);
- return ERR_CACHE_MISS;
- }
- // We don't have the whole resource.
- 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 ((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;
- }
- // Bail out!
- TransitionToState(STATE_SEND_REQUEST);
- mode_ = NONE;
- return OK;
- }
- if (truncated_) {
- // Truncated entries can cause partial gets, so we shouldn't record this
- // load in histograms.
- UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
- skip_validation = !partial_->initial_validation();
- }
- // If this is the first request (!reading_) of a 206 entry (is_sparse_) that
- // doesn't actually cover the entire file (which with !reading would require
- // partial->IsLastRange()), and the user is requesting the whole thing
- // (!partial_->range_requested()), make sure to validate the first chunk,
- // since afterwards it will be too late if it's actually out-of-date (or the
- // server bungles invalidation). This is limited to the whole-file request
- // as a targeted fix for https://crbug.com/888742 while avoiding extra
- // requests in other cases, but the problem can occur more generally as well;
- // it's just a lot less likely with applications actively using ranges.
- // See https://crbug.com/902724 for the more general case.
- 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)) {
- // Force revalidation for sparse or truncated entries. Note that we don't
- // want to ignore the regular validation logic just because a byte range was
- // part of the request.
- 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 {
- // Make the network request conditional, to see if we may reuse our cached
- // response. If we cannot do so, then we just resort to a normal fetch.
- // Our mode remains READ_WRITE for a conditional request. Even if the
- // conditionalization fails, we don't switch to WRITE mode until we
- // know we won't be falling back to using the cache entry in the
- // LOAD_FROM_CACHE_IF_OFFLINE case.
- 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();
- // Partial requests should not be recorded in histograms.
- UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
- if (method_ == "HEAD")
- return BeginCacheValidation();
- if (!range_requested_) {
- // The request is not for a range, but we have stored just ranges.
- 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;
- }
- // This should only be called once per request.
- 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()) {
- // The stored data is fine, but the request may be invalid.
- 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;
- // Retrieve either the cached response's "etag" or "last-modified" header.
- 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()) {
- // The externally conditionalized request is not a validation request
- // for our existing cache entry. Proceed with caching disabled.
- 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;
- }
- ValidationType HttpCache::Transaction::RequiresValidation() {
- // TODO(darin): need to do more work here:
- // - make sure we have a matching request method
- // - watch out for cached responses that depend on authentication
- 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;
- base::TimeDelta response_time_in_cache =
- cache_->clock_->Now() - response_.response_time;
- if (response_.unused_since_prefetch &&
- !(effective_load_flags_ & LOAD_PREFETCH) &&
- (response_time_in_cache < base::Minutes(kPrefetchReuseMins)) &&
- (response_time_in_cache >= base::TimeDelta())) {
- // The first use of a resource after prefetch within a short window skips
- // validation.
- return VALIDATION_NONE;
- }
- if (effective_load_flags_ & LOAD_VALIDATE_CACHE) {
- validation_cause_ = VALIDATION_CAUSE_VALIDATE_FLAG;
- return VALIDATION_SYNCHRONOUS;
- }
- if (method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH")
- return VALIDATION_SYNCHRONOUS;
- ValidationType validation_required_by_headers =
- response_.headers->RequiresValidation(response_.request_time,
- response_.response_time,
- cache_->clock_->Now());
- 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) {
- // Asynchronous revalidation is only supported for GET methods.
- if (request_->method != "GET")
- return VALIDATION_SYNCHRONOUS;
- // If the timeout on the staleness revalidation is set don't hand out
- // a resource that hasn't been async validated.
- 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());
- // This only makes sense for cached 200 or 206 responses.
- if (response_.headers->response_code() != net::HTTP_OK &&
- response_.headers->response_code() != net::HTTP_PARTIAL_CONTENT) {
- return false;
- }
- // Just use the first available ETag and/or Last-Modified header value.
- // TODO(darin): Or should we use the last?
- 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 {
- // These methods indicate that we should only try to open an entry and not
- // fallback to create.
- 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_) {
- // Can't rely on last-modified if vary is different.
- last_modified_value.clear();
- if (etag_value.empty())
- return false;
- }
- if (!partial_) {
- // Need to customize the request, so this forces us to allocate :(
- 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) {
- // We don't want to switch to WRITE mode if we don't have this block of a
- // byte-range request because we may have other parts cached.
- custom_request_->extra_headers.SetHeader(
- HttpRequestHeaders::kIfRange, etag_value);
- } else {
- custom_request_->extra_headers.SetHeader(
- HttpRequestHeaders::kIfNoneMatch, etag_value);
- }
- // For byte-range requests, make sure that we use only one way to validate
- // the request.
- 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) {
- // Not going to be clever with those...
- if (partial_)
- return false;
- // Avoiding open based on in-memory hints requires us to be permitted to
- // modify the cache, including deleting an old entry. Only the READ_WRITE
- // and WRITE modes permit that... and WRITE never tries to open entries in the
- // first place, so we shouldn't see it here.
- DCHECK_NE(mode_, WRITE);
- if (mode_ != READ_WRITE)
- return false;
- // If we are loading ignoring cache validity (aka back button), obviously
- // can't reject things based on it. Also if LOAD_ONLY_FROM_CACHE there is no
- // hope of network offering anything better.
- 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() {
- // unused_since_prefetch overrides some caching headers, so it may be useful
- // regardless of what they say.
- if (response_.unused_since_prefetch)
- return false;
- // Has an e-tag or last-modified: we can probably send a conditional request,
- // so it's potentially useful.
- std::string etag_ignored, last_modified_ignored;
- if (IsResponseConditionalizable(&etag_ignored, &last_modified_ignored))
- return false;
- // If none of the above is true and the entry has zero freshness, then it
- // won't be usable absent load flag override.
- return response_.headers->GetFreshnessLifetimes(response_.response_time)
- .freshness.is_zero();
- }
- // We just received some headers from the server. We may have asked for a range,
- // in which case partial_ has an object. This could be the first network request
- // we make to fulfill the original request, or we may be already reading (from
- // the net and / or the cache). If we are not expecting a certain response, we
- // just bypass the cache for this request (but again, maybe we are reading), and
- // delete partial_ (so we are not able to "fix" the headers that we return to
- // the user). This results in either a weird response for the caller (we don't
- // expect it after all), or maybe a range that was not exactly what it was asked
- // for.
- //
- // If the server is simply telling us that the resource has changed, we delete
- // the cached entry and restart the request as the caller intended (by returning
- // false from this method). However, we may not be able to do that at any point,
- // for instance if we already returned the headers to the user.
- //
- // WARNING: Whenever this code returns false, it has to make sure that the next
- // time it is called it will return true so that we don't keep retrying the
- // request.
- 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_) {
- // We gave up trying to match this request with the stored data. If the
- // server is ok with the request, delete the entry, otherwise just ignore
- // this request
- DCHECK(!reading_);
- if (partial_response || response_code == net::HTTP_OK) {
- DoomPartialEntry(true);
- mode_ = NONE;
- } else {
- if (response_code == net::HTTP_NOT_MODIFIED) {
- // Change the response code of the request to be 416 (Requested range
- // not satisfiable).
- SetResponse(*new_response_);
- partial_->FixResponseHeaders(response_.headers.get(), false);
- }
- IgnoreRangeRequest();
- }
- return true;
- }
- if (!partial_) {
- // We are not expecting 206 but we may have one.
- if (partial_response)
- IgnoreRangeRequest();
- return true;
- }
- // TODO(rvargas): Do we need to consider other results here?.
- bool failure = response_code == net::HTTP_OK ||
- response_code == net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE;
- if (partial_->IsCurrentRangeCached()) {
- // We asked for "If-None-Match: " so a 206 means a new object.
- if (partial_response)
- failure = true;
- if (response_code == net::HTTP_NOT_MODIFIED &&
- partial_->ResponseHeadersOK(headers))
- return true;
- } else {
- // We asked for "If-Range: " so a 206 means just another range.
- if (partial_response) {
- if (partial_->ResponseHeadersOK(headers)) {
- handling_206_ = true;
- return true;
- } else {
- failure = true;
- }
- }
- if (!reading_ && !is_sparse_ && !partial_response) {
- // See if we can ignore the fact that we issued a byte range request.
- // If the server sends 200, just store it. If it sends an error, redirect
- // or something else, we may store the response as long as we didn't have
- // anything already stored.
- if (response_code == net::HTTP_OK ||
- (!truncated_ && response_code != net::HTTP_NOT_MODIFIED &&
- response_code != net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE)) {
- // The server is sending something else, and we can save it.
- DCHECK((truncated_ && !partial_->IsLastRange()) || range_requested_);
- partial_.reset();
- truncated_ = false;
- return true;
- }
- }
- // 304 is not expected here, but we'll spare the entry (unless it was
- // truncated).
- if (truncated_)
- failure = true;
- }
- if (failure) {
- // We cannot truncate this entry, it has to be deleted.
- UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
- mode_ = NONE;
- if (is_sparse_ || truncated_) {
- // There was something cached to start with, either sparsed data (206), or
- // a truncated 200, which means that we probably modified the request,
- // adding a byte range or modifying the range requested by the caller.
- if (!reading_ && !partial_->IsLastRange()) {
- // We have not returned anything to the caller yet so it should be safe
- // to issue another network request, this time without us messing up the
- // headers.
- ResetPartialState(true);
- return false;
- }
- LOG(WARNING) << "Failed to revalidate partial entry";
- }
- DoomPartialEntry(true);
- return true;
- }
- IgnoreRangeRequest();
- return true;
- }
- void HttpCache::Transaction::IgnoreRangeRequest() {
- // We have a problem. We may or may not be reading already (in which case we
- // returned the headers), but we'll just pretend that this request is not
- // using the cache and see what happens. Most likely this is the first
- // response from the server (it's not changing its mind midway, right?).
- UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
- DoneWithEntry(mode_ != WRITE);
- partial_.reset(nullptr);
- }
- // Called to signal to the consumer that we are about to read headers from a
- // cached entry originally read from a given IP endpoint.
- 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_PRIVATE_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 {
- // Release the entry for further use - we are done using it.
- DoneWithEntry(/*entry_is_complete=*/true);
- }
- TransitionToState(STATE_NONE);
- return result;
- }
- if (reading_) {
- // We can only get here if we're reading a partial range of bytes from the
- // cache. In that case, proceed to read the bytes themselves.
- DCHECK(partial_);
- TransitionToState(STATE_CACHE_READ_DATA);
- } else {
- // Otherwise, we have just read headers from the cache.
- TransitionToState(STATE_SETUP_ENTRY_FOR_READ);
- }
- return OK;
- }
- void HttpCache::Transaction::DoomInconsistentEntry() {
- // Explicitly call `DoomActiveEntry()` ourselves before calling
- // `DoneWithEntry()` because we cannot rely on the latter doing it for us.
- // Indeed, `DoneWithEntry(false)` does not call `DoomActiveEntry()` if either
- // of the following conditions hold:
- //
- // - the transaction uses the cache in read-only mode
- // - the transaction has passed the headers phase and is reading
- //
- // Inconsistent cache entries can cause deterministic failures even in
- // read-only mode, so they should be doomed anyway. They can also be detected
- // during the reading phase in the case of split range requests, since those
- // requests can result in multiple connections being obtained to different
- // remote endpoints.
- cache_->DoomActiveEntry(cache_key_);
- DoneWithEntry(/*entry_is_complete=*/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_) {
- // Entry got destroyed when twiddling SWR bits.
- 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))) {
- // We are going to return the saved response headers to the caller, so
- // we may need to adjust them first. In cases we are handling a range
- // request to a regular entry, we want the response to be a 200 or 206,
- // since others can't really be turned into a 206.
- 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);
- // Do not cache content with cert errors. This is to prevent not reporting net
- // errors when loading a resource from the cache. When we load a page over
- // HTTPS with a cert error we show an SSL blocking page. If the user clicks
- // proceed we reload the resource ignoring the errors. The loaded resource is
- // then cached. If that resource is subsequently loaded from the cache, no
- // net error is reported (even though the cert status contains the actual
- // errors) and no SSL blocking page is shown. An alternative would be to
- // reverse-map the cert status to a net error and replay the net error.
- 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());
- // When writing headers, we normally only write the non-transient headers.
- 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();
- // Summarize some info on cacheability in memory. Don't do it if doomed
- // since then |entry_| isn't definitive for |cache_key_|.
- if (!entry_->doomed) {
- cache_->GetCurrentBackend()->SetEntryInMemoryData(
- cache_key_, ComputeUnusablePerCachingHeaders()
- ? HINT_UNUSABLE_PER_CACHING_HEADERS
- : 0);
- }
- return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(),
- io_buf_len_, io_callback_, true);
- }
- int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) {
- 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;
- // Let writers know so that it doesn't attempt to write to the cache.
- if (InWriters()) {
- stopped = entry_->writers->StopCaching(success /* keep_entry */);
- if (stopped)
- mode_ = NONE;
- } else if (entry_) {
- stopped = true;
- DoneWithEntry(success /* entry_is_complete */);
- }
- 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; // switch to 'pass through' mode
- }
- void HttpCache::Transaction::DoneWithEntryForRestartWithCache() {
- if (!entry_)
- return;
- cache_->DoneWithEntry(entry_, this, /*entry_is_complete=*/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);
- }
- // Avoid using this entry in the future.
- if (cache_.get())
- cache_->DoomActiveEntry(cache_key_);
- if (restart) {
- DCHECK(!reading_);
- DCHECK(!network_trans_.get());
- // Since we are going to add this to a new entry, not recording histograms
- // or setting mode to NONE at this point by invoking the wrapper
- // DoneWithEntry.
- cache_->DoneWithEntry(entry_, this, true /* entry_is_complete */,
- partial_ != nullptr);
- entry_ = nullptr;
- is_sparse_ = false;
- // It's OK to use PartialData::RestoreHeaders here as |restart| is only set
- // when the HttpResponseInfo couldn't even be read, at which point it's
- // too early for range info in |partial_| to have changed.
- 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 /* entry_is_complete */);
- 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 /* entry_is_complete */,
- 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) {
- // We need to move on to the next range.
- TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION);
- } else if (result < 0) {
- return OnCacheReadError(result, false);
- } else {
- TransitionToState(STATE_NONE);
- }
- return result;
- }
- int HttpCache::Transaction::DoRestartPartialRequest() {
- // The stored data cannot be used. Get rid of it and restart this request.
- net_log_.AddEvent(NetLogEventType::HTTP_CACHE_RESTART_PARTIAL_REQUEST);
- // WRITE + Doom + STATE_INIT_ENTRY == STATE_CREATE_ENTRY (without an attempt
- // to Doom the entry again).
- ResetPartialState(!range_requested_);
- // Change mode to WRITE after ResetPartialState as that may have changed the
- // mode to NONE.
- 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) {
- // The simplest way to re-initialize partial_ is to create a new object.
- partial_ = std::make_unique<PartialData>();
- // Reset the range header to the original value (http://crbug.com/820599).
- 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());
- }
- // Histogram data from the end of 2010 show the following distribution of
- // response headers:
- //
- // Content-Length............... 87%
- // Date......................... 98%
- // Last-Modified................ 49%
- // Etag......................... 19%
- // Accept-Ranges: bytes......... 25%
- // Accept-Ranges: none.......... 0.4%
- // Strong Validator............. 50%
- // Strong Validator + ranges.... 24%
- // Strong Validator + CL........ 49%
- //
- bool HttpCache::Transaction::CanResume(bool has_data) {
- // Double check that there is something worth keeping.
- if (has_data && !entry_->GetEntry()->GetDataSize(kResponseContentIndex))
- return false;
- if (method_ != "GET")
- return false;
- // Note that if this is a 206, content-length was already fixed after calling
- // PartialData::ResponseHeadersOK().
- 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 validation_request =
- cache_entry_status_ == CacheEntryStatus::ENTRY_VALIDATED ||
- cache_entry_status_ == CacheEntryStatus::ENTRY_UPDATED;
- bool is_third_party = false;
- // Given that cache_entry_status_ is not ENTRY_UNDEFINED, the request must
- // have started and so request_ should exist.
- 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();
- if (response_headers && response_headers->GetMimeType(&mime_type)) {
- // Record the cache pattern by resource type. The type is inferred by
- // response header mime type, which could be incorrect, so this is just an
- // estimate.
- if (mime_type == "text/html" &&
- (effective_load_flags_ & LOAD_MAIN_FRAME_DEPRECATED)) {
- CACHE_STATUS_HISTOGRAMS(".MainFrameHTML");
- } 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("");
- if (validation_request) {
- UMA_HISTOGRAM_ENUMERATION("HttpCache.ValidationCause", validation_cause_,
- VALIDATION_CAUSE_MAX);
- }
- if (cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE) {
- UMA_HISTOGRAM_ENUMERATION("HttpCache.CantConditionalizeCause",
- validation_cause_, VALIDATION_CAUSE_MAX);
- }
- 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();
- 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_;
- base::TimeDelta after_send_time = now - send_request_since_;
- UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SentRequest", total_time);
- UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend", before_send_time);
- // TODO(gavinp): Remove or minimize these histograms, particularly the ones
- // below this comment after we have received initial data.
- switch (cache_entry_status_) {
- case CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE: {
- UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.CantConditionalize",
- before_send_time);
- UMA_HISTOGRAM_TIMES("HttpCache.AfterSend.CantConditionalize",
- after_send_time);
- break;
- }
- case CacheEntryStatus::ENTRY_NOT_IN_CACHE: {
- UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.NotCached", before_send_time);
- UMA_HISTOGRAM_TIMES("HttpCache.AfterSend.NotCached", after_send_time);
- break;
- }
- case CacheEntryStatus::ENTRY_VALIDATED: {
- UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Validated", before_send_time);
- UMA_HISTOGRAM_TIMES("HttpCache.AfterSend.Validated", after_send_time);
- break;
- }
- case CacheEntryStatus::ENTRY_UPDATED: {
- UMA_HISTOGRAM_TIMES("HttpCache.AfterSend.Updated", after_send_time);
- UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Updated", before_send_time);
- break;
- }
- default:
- NOTREACHED();
- }
- }
- 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) {
- // Ensure that the state is only set once per Do* 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 {
- // Do not cache no-store content.
- 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())) {
- // If the feature is always enabled or enabled while we're running on
- // battery, and the acquired content is 'large' and not already cached, and
- // we have a MIME type of audio or video, then disable the cache for this
- // response. We based our initial definition of 'large' on the disk cache
- // maximum block size of 16K, which we observed captures the majority of
- // responses from various MSE implementations.
- 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() {
- // Because of COEP, we need to add CORP to the 304 of resources that set it
- // previously. It will be blocked in the network service otherwise.
- 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);
- // For efficiency and concision, we list known headers matching a wildcard
- // explicitly rather than doing prefix matching.
- 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",
- });
- // Pairs of (lower_case_header_name, header_value).
- std::vector<std::pair<std::string, std::string>> filtered_headers;
- // It's good to set the initial allocation size of the vector to the
- // expected size to avoid a lot of reallocations. This value was chosen as
- // it is a nice round number.
- filtered_headers.reserve(16);
- {
- // Iterate the response headers looking for matches.
- 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_));
- }
- } // namespace net
|