network_service_memory_cache_unittest.cc 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <memory>
  5. #include "base/memory/memory_pressure_listener.h"
  6. #include "base/run_loop.h"
  7. #include "base/strings/string_number_conversions.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "base/test/bind.h"
  10. #include "base/test/scoped_feature_list.h"
  11. #include "base/test/task_environment.h"
  12. #include "mojo/public/cpp/bindings/remote.h"
  13. #include "mojo/public/cpp/system/data_pipe_utils.h"
  14. #include "net/base/features.h"
  15. #include "net/base/mime_sniffer.h"
  16. #include "net/base/schemeful_site.h"
  17. #include "net/base/url_util.h"
  18. #include "net/test/embedded_test_server/embedded_test_server.h"
  19. #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
  20. #include "net/url_request/url_request_context.h"
  21. #include "net/url_request/url_request_context_builder.h"
  22. #include "services/network/cors/cors_url_loader_factory.h"
  23. #include "services/network/network_context.h"
  24. #include "services/network/network_service.h"
  25. #include "services/network/network_service_memory_cache.h"
  26. #include "services/network/network_service_memory_cache_writer.h"
  27. #include "services/network/public/cpp/cors/origin_access_list.h"
  28. #include "services/network/public/cpp/cross_origin_embedder_policy.h"
  29. #include "services/network/public/cpp/features.h"
  30. #include "services/network/public/mojom/http_raw_headers.mojom.h"
  31. #include "services/network/public/mojom/url_loader.mojom.h"
  32. #include "services/network/public/mojom/url_loader_factory.mojom.h"
  33. #include "services/network/resource_scheduler/resource_scheduler_client.h"
  34. #include "services/network/test/fake_test_cert_verifier_params_factory.h"
  35. #include "services/network/test/mock_devtools_observer.h"
  36. #include "services/network/test/test_url_loader_client.h"
  37. #include "services/network/test/test_utils.h"
  38. #include "testing/gtest/include/gtest/gtest.h"
  39. #include "url/gurl.h"
  40. #include "url/origin.h"
  41. namespace network {
  42. namespace {
  43. constexpr int kMaxTotalSize = 8 * 1024;
  44. constexpr int kMaxPerEntrySize = 4 * 1024;
  45. struct LoaderPair {
  46. LoaderPair() : client(std::make_unique<TestURLLoaderClient>()) {}
  47. ~LoaderPair() = default;
  48. LoaderPair(LoaderPair&&) = default;
  49. LoaderPair& operator=(LoaderPair&&) = default;
  50. LoaderPair(const LoaderPair&) = delete;
  51. LoaderPair& operator=(const LoaderPair&) = delete;
  52. mojo::Remote<mojom::URLLoader> loader_remote;
  53. std::unique_ptr<TestURLLoaderClient> client;
  54. };
  55. mojom::URLResponseHeadPtr CreateCacheableURLResponseHead() {
  56. mojom::URLResponseHeadPtr response_head = CreateURLResponseHead(net::HTTP_OK);
  57. response_head->headers->AddHeader("Cache-Control", "max-age=60");
  58. base::Time now = base::Time::Now();
  59. response_head->request_time = now;
  60. response_head->response_time = now;
  61. return response_head;
  62. }
  63. // An EmbeddedTestServer request handler that returns a cacheable response of
  64. // which body size and max-age are specified by the query string. The content
  65. // body consists of 'a'.
  66. std::unique_ptr<net::test_server::HttpResponse> CacheableResponseHandler(
  67. const net::test_server::HttpRequest& request) {
  68. if (request.GetURL().path_piece() != "/cacheable")
  69. return nullptr;
  70. auto response = std::make_unique<net::test_server::BasicHttpResponse>();
  71. uint64_t body_size = 64;
  72. std::string query_body_size;
  73. if (net::GetValueForKeyInQuery(request.GetURL(), "body-size",
  74. &query_body_size)) {
  75. EXPECT_TRUE(base::StringToUint64(query_body_size, &body_size));
  76. }
  77. uint64_t max_age = 60;
  78. std::string query_max_age;
  79. if (net::GetValueForKeyInQuery(request.GetURL(), "max-age", &query_max_age)) {
  80. EXPECT_TRUE(base::StringToUint64(query_max_age, &max_age));
  81. }
  82. response->AddCustomHeader("cache-control",
  83. base::StringPrintf("max-age=%" PRId64, max_age));
  84. response->set_content(std::string(body_size, 'a'));
  85. return response;
  86. }
  87. // Similar to above, but doesn't send Content-Length header.
  88. std::unique_ptr<net::test_server::HttpResponse>
  89. CacheableWithoutContentLengthHandler(
  90. const net::test_server::HttpRequest& request) {
  91. if (request.GetURL().path_piece() != "/cacheable_without_content_length")
  92. return nullptr;
  93. uint64_t body_size = 64;
  94. std::string query_body_size;
  95. if (net::GetValueForKeyInQuery(request.GetURL(), "body-size",
  96. &query_body_size)) {
  97. EXPECT_TRUE(base::StringToUint64(query_body_size, &body_size));
  98. }
  99. constexpr const char kHeader[] =
  100. "HTTP/1.1 200 OK\n"
  101. "Content-Type: text/plain\n";
  102. auto response = std::make_unique<net::test_server::RawHttpResponse>(
  103. kHeader, std::string(body_size, 'a'));
  104. return response;
  105. }
  106. // Used for cross origin read blocking check.
  107. std::unique_ptr<net::test_server::HttpResponse> CorbCheckHandler(
  108. const net::test_server::HttpRequest& request) {
  109. if (request.GetURL().path_piece() == "/corb_nosniff") {
  110. auto response = std::make_unique<net::test_server::BasicHttpResponse>();
  111. response->AddCustomHeader("cache-control", "max-age=60");
  112. response->AddCustomHeader("x-content-type-options", "nosniff");
  113. response->AddCustomHeader("content-type", "text/javascript");
  114. response->set_content("{\"key\": true}");
  115. return response;
  116. }
  117. if (request.GetURL().path_piece() == "/corb_sniff") {
  118. auto response = std::make_unique<net::test_server::BasicHttpResponse>();
  119. response->AddCustomHeader("cache-control", "max-age=60");
  120. response->AddCustomHeader("content-type", "text/html");
  121. // Set a html content of which size is larger than net::kMaxBytestosniff.
  122. std::string content("<html>sniffed content");
  123. content.append(std::string(net::kMaxBytesToSniff, ' '));
  124. response->set_content(content);
  125. return response;
  126. }
  127. return nullptr;
  128. }
  129. // Used in NetworkServiceMemoryCacheWithFactoryOverrideTest.
  130. class TestURLLoaderFactory : public mojom::URLLoaderFactory {
  131. public:
  132. explicit TestURLLoaderFactory(
  133. mojo::PendingReceiver<mojom::URLLoaderFactory> receiver) {
  134. receivers_.Add(this, std::move(receiver));
  135. }
  136. void CreateLoaderAndStart(
  137. mojo::PendingReceiver<mojom::URLLoader> receiver,
  138. int32_t request_id,
  139. uint32_t options,
  140. const ResourceRequest& resource_request,
  141. mojo::PendingRemote<mojom::URLLoaderClient> pending_client,
  142. const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
  143. override {
  144. url_loader_receivers_.Add(&noop_loader_, std::move(receiver));
  145. mojo::Remote<mojom::URLLoaderClient> client(std::move(pending_client));
  146. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  147. client->OnReceiveResponse(std::move(response_head), /*body=*/{});
  148. client->OnComplete(URLLoaderCompletionStatus(net::OK));
  149. }
  150. void Clone(mojo::PendingReceiver<mojom::URLLoaderFactory> receiver) override {
  151. receivers_.Add(this, std::move(receiver));
  152. }
  153. private:
  154. class NoopURLLoader : public mojom::URLLoader {
  155. public:
  156. void FollowRedirect(
  157. const std::vector<std::string>& removed_headers,
  158. const net::HttpRequestHeaders& modified_headers,
  159. const net::HttpRequestHeaders& modified_cors_exempt_headers,
  160. const absl::optional<GURL>& new_url) override {}
  161. void SetPriority(net::RequestPriority priority,
  162. int32_t intra_priority_value) override {}
  163. void PauseReadingBodyFromNet() override {}
  164. void ResumeReadingBodyFromNet() override {}
  165. };
  166. NoopURLLoader noop_loader_;
  167. mojo::ReceiverSet<mojom::URLLoaderFactory> receivers_;
  168. mojo::ReceiverSet<mojom::URLLoader> url_loader_receivers_;
  169. };
  170. } // namespace
  171. class NetworkServiceMemoryCacheTest : public testing::Test {
  172. public:
  173. NetworkServiceMemoryCacheTest()
  174. : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {
  175. scoped_feature_list_.InitAndEnableFeatureWithParameters(
  176. features::kNetworkServiceMemoryCache,
  177. {{"max_total_size", base::NumberToString(kMaxTotalSize)},
  178. {"max_per_entry_size", base::NumberToString(kMaxPerEntrySize)}});
  179. }
  180. ~NetworkServiceMemoryCacheTest() override = default;
  181. void SetUp() override {
  182. should_redirect_in_cacheable_handler_ = false;
  183. test_server_.AddDefaultHandlers();
  184. test_server_.RegisterRequestHandler(
  185. base::BindRepeating(&CacheableResponseHandler));
  186. test_server_.RegisterRequestHandler(
  187. base::BindRepeating(&CacheableWithoutContentLengthHandler));
  188. test_server_.RegisterRequestHandler(base::BindRepeating(&CorbCheckHandler));
  189. test_server_.RegisterRequestHandler(base::BindRepeating(
  190. &NetworkServiceMemoryCacheTest::CacheableOrRedirectHandler,
  191. base::Unretained(this)));
  192. ASSERT_TRUE(test_server_.Start());
  193. // The following setup similar to CorsURLLoaderFactoryTest.
  194. network_service_ = NetworkService::CreateForTesting();
  195. auto context_params = mojom::NetworkContextParams::New();
  196. context_params->cert_verifier_params =
  197. FakeTestCertVerifierParamsFactory::GetCertVerifierParams();
  198. context_params->initial_proxy_config =
  199. net::ProxyConfigWithAnnotation::CreateDirect();
  200. network_context_ = std::make_unique<NetworkContext>(
  201. network_service_.get(),
  202. network_context_remote_.BindNewPipeAndPassReceiver(),
  203. std::move(context_params));
  204. auto factory_params = network::mojom::URLLoaderFactoryParams::New();
  205. constexpr int kProcessId = 123;
  206. factory_params->process_id = kProcessId;
  207. factory_params->is_trusted = true;
  208. factory_params->request_initiator_origin_lock =
  209. url::Origin::Create(test_server_.base_url());
  210. if (HasFactoryOverride()) {
  211. factory_params->factory_override = mojom::URLLoaderFactoryOverride::New();
  212. mojo::PendingRemote<mojom::URLLoaderFactory> factory_remote;
  213. overriding_factory_ = std::make_unique<TestURLLoaderFactory>(
  214. factory_remote.InitWithNewPipeAndPassReceiver());
  215. factory_params->factory_override->overriding_factory =
  216. std::move(factory_remote);
  217. }
  218. url::Origin test_server_origin =
  219. url::Origin::Create(test_server_.base_url());
  220. factory_params->isolation_info =
  221. net::IsolationInfo::CreateForInternalRequest(test_server_origin);
  222. cors_url_loader_factory_ = std::make_unique<cors::CorsURLLoaderFactory>(
  223. network_context_.get(), std::move(factory_params),
  224. /*resource_scheduler_client=*/nullptr,
  225. cors_url_loader_factory_remote_.BindNewPipeAndPassReceiver(),
  226. &origin_access_list_);
  227. }
  228. base::test::TaskEnvironment& task_environment() { return task_environment_; }
  229. net::test_server::EmbeddedTestServer& test_server() { return test_server_; }
  230. net::URLRequestContext& url_request_context() {
  231. return *network_context_->url_request_context();
  232. }
  233. NetworkServiceMemoryCache& memory_cache() {
  234. return *network_context_->GetMemoryCache();
  235. }
  236. void MakeCacheableHandlerSendRedirect() {
  237. should_redirect_in_cacheable_handler_ = true;
  238. }
  239. ResourceRequest CreateRequest(const std::string& relative_path) {
  240. ResourceRequest request;
  241. GURL url = test_server().GetURL(relative_path);
  242. url::Origin origin = url::Origin::Create(url);
  243. request.url = url;
  244. request.request_initiator = origin;
  245. request.enable_load_timing = true;
  246. return request;
  247. }
  248. std::unique_ptr<net::URLRequest> CreateURLRequest(const GURL& url) {
  249. return url_request_context().CreateRequest(url, net::DEFAULT_PRIORITY,
  250. /*delegate=*/nullptr,
  251. TRAFFIC_ANNOTATION_FOR_TESTS);
  252. }
  253. LoaderPair CreateLoaderAndStart(const ResourceRequest& request) {
  254. LoaderPair pair;
  255. cors_url_loader_factory_->CreateLoaderAndStart(
  256. pair.loader_remote.BindNewPipeAndPassReceiver(), /*request_id=*/1,
  257. mojom::kURLLoadOptionNone, request, pair.client->CreateRemote(),
  258. net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
  259. return pair;
  260. }
  261. void StoreResponseToMemoryCache(const ResourceRequest& request) {
  262. LoaderPair pair = CreateLoaderAndStart(request);
  263. pair.client->RunUntilComplete();
  264. }
  265. bool WriterWillBeCreatedToStoreResponse(
  266. net::URLRequest* url_request,
  267. const mojom::URLResponseHeadPtr& response_head) {
  268. std::unique_ptr<NetworkServiceMemoryCacheWriter> writer =
  269. memory_cache().MaybeCreateWriter(url_request,
  270. mojom::RequestDestination::kDocument,
  271. net::TransportInfo(), response_head);
  272. return writer.get() != nullptr;
  273. }
  274. bool CanServeFromMemoryCache(const ResourceRequest& request) {
  275. net::SchemefulSite site(request.url);
  276. net::NetworkIsolationKey network_isolation_key(/*top_frame_site=*/site,
  277. /*frame_site=*/site);
  278. return CanServeFromMemoryCache(request, network_isolation_key);
  279. }
  280. bool CanServeFromMemoryCache(
  281. const ResourceRequest& request,
  282. const net::NetworkIsolationKey& network_isolation_key) {
  283. return CanServeFromMemoryCache(request, network_isolation_key,
  284. CrossOriginEmbedderPolicy());
  285. }
  286. bool CanServeFromMemoryCache(
  287. const ResourceRequest& request,
  288. const net::NetworkIsolationKey& network_isolation_key,
  289. const CrossOriginEmbedderPolicy& cross_origin_embedder_policy) {
  290. return memory_cache()
  291. .CanServe(mojom::kURLLoadOptionNone, request, network_isolation_key,
  292. cross_origin_embedder_policy,
  293. /*client_security_state=*/nullptr)
  294. .has_value();
  295. }
  296. virtual bool HasFactoryOverride() const { return false; }
  297. private:
  298. std::unique_ptr<net::test_server::HttpResponse> CacheableOrRedirectHandler(
  299. const net::test_server::HttpRequest& request) {
  300. if (request.GetURL().path_piece() != "/cacheable_or_redirect")
  301. return nullptr;
  302. auto response = std::make_unique<net::test_server::BasicHttpResponse>();
  303. if (should_redirect_in_cacheable_handler_) {
  304. response->set_code(net::HttpStatusCode::HTTP_FOUND);
  305. response->AddCustomHeader("location", "/cacheable");
  306. } else {
  307. constexpr size_t kBodySize = 64;
  308. response->AddCustomHeader("cache-control", "max-age=60");
  309. response->set_content(std::string(kBodySize, 'a'));
  310. }
  311. return response;
  312. }
  313. base::test::ScopedFeatureList scoped_feature_list_;
  314. base::test::TaskEnvironment task_environment_;
  315. std::unique_ptr<net::URLRequestContext> url_request_context_;
  316. std::unique_ptr<NetworkService> network_service_;
  317. std::unique_ptr<NetworkContext> network_context_;
  318. mojo::Remote<mojom::NetworkContext> network_context_remote_;
  319. net::test_server::EmbeddedTestServer test_server_;
  320. bool should_redirect_in_cacheable_handler_ = false;
  321. std::unique_ptr<mojom::URLLoaderFactory> cors_url_loader_factory_;
  322. mojo::Remote<mojom::URLLoaderFactory> cors_url_loader_factory_remote_;
  323. cors::OriginAccessList origin_access_list_;
  324. std::unique_ptr<TestURLLoaderFactory> overriding_factory_;
  325. };
  326. class NetworkServiceMemoryCacheWithFactoryOverrideTest
  327. : public NetworkServiceMemoryCacheTest {
  328. public:
  329. bool HasFactoryOverride() const override { return true; }
  330. };
  331. TEST_F(NetworkServiceMemoryCacheTest,
  332. CreateWriter_SchemeIsNeitherHTTPNorHTTPS) {
  333. std::unique_ptr<net::URLRequest> url_request =
  334. CreateURLRequest(GURL("data:text/plain;foo"));
  335. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  336. ASSERT_FALSE(
  337. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  338. }
  339. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_MethodIsNotGet) {
  340. std::unique_ptr<net::URLRequest> url_request =
  341. CreateURLRequest(test_server().GetURL("/cacheable"));
  342. url_request->set_method("POST");
  343. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  344. ASSERT_FALSE(
  345. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  346. }
  347. TEST_F(NetworkServiceMemoryCacheTest,
  348. CreateWriter_NetworkIsolationKeyIsTransient) {
  349. base::test::ScopedFeatureList feature_list;
  350. feature_list.InitAndEnableFeature(
  351. net::features::kSplitCacheByNetworkIsolationKey);
  352. std::unique_ptr<net::URLRequest> url_request =
  353. CreateURLRequest(test_server().GetURL("/cacheable"));
  354. url_request->set_isolation_info(net::IsolationInfo::CreateTransient());
  355. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  356. ASSERT_FALSE(
  357. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  358. }
  359. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_StatusCodeIsNotOK) {
  360. std::unique_ptr<net::URLRequest> url_request =
  361. CreateURLRequest(test_server().GetURL("/cacheable"));
  362. mojom::URLResponseHeadPtr response_head =
  363. CreateURLResponseHead(net::HTTP_BAD_REQUEST);
  364. ASSERT_FALSE(
  365. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  366. }
  367. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_EmptyResponse) {
  368. std::unique_ptr<net::URLRequest> url_request =
  369. CreateURLRequest(test_server().GetURL("/cacheable"));
  370. auto response_head = mojom::URLResponseHead::New();
  371. ASSERT_FALSE(
  372. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  373. }
  374. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_BypassCache) {
  375. std::unique_ptr<net::URLRequest> url_request =
  376. CreateURLRequest(test_server().GetURL("/cacheable"));
  377. url_request->SetLoadFlags(net::LOAD_BYPASS_CACHE);
  378. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  379. ASSERT_FALSE(
  380. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  381. }
  382. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_DisableCache) {
  383. std::unique_ptr<net::URLRequest> url_request =
  384. CreateURLRequest(test_server().GetURL("/cacheable"));
  385. url_request->SetLoadFlags(net::LOAD_DISABLE_CACHE);
  386. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  387. ASSERT_FALSE(
  388. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  389. }
  390. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_ResponseNotCacheable) {
  391. std::unique_ptr<net::URLRequest> url_request =
  392. CreateURLRequest(test_server().GetURL("/cacheable"));
  393. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  394. response_head->headers->RemoveHeader("cache-control");
  395. response_head->headers->AddHeader("cache-control", "no-store");
  396. ASSERT_FALSE(
  397. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  398. }
  399. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_IfUnmodifiedSince) {
  400. std::unique_ptr<net::URLRequest> url_request =
  401. CreateURLRequest(test_server().GetURL("/cacheable"));
  402. url_request->SetExtraRequestHeaderByName("iF-unMOdified-since", "hello",
  403. /*overwrite=*/true);
  404. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  405. ASSERT_FALSE(
  406. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  407. }
  408. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_IfMatch) {
  409. std::unique_ptr<net::URLRequest> url_request =
  410. CreateURLRequest(test_server().GetURL("/cacheable"));
  411. url_request->SetExtraRequestHeaderByName("IF-match", "foo",
  412. /*overwrite=*/true);
  413. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  414. ASSERT_FALSE(
  415. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  416. }
  417. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_IfRange) {
  418. std::unique_ptr<net::URLRequest> url_request =
  419. CreateURLRequest(test_server().GetURL("/cacheable"));
  420. url_request->SetExtraRequestHeaderByName("if-rangE", "bar",
  421. /*overwrite=*/true);
  422. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  423. ASSERT_FALSE(
  424. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  425. }
  426. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_IfModifiedSince) {
  427. std::unique_ptr<net::URLRequest> url_request =
  428. CreateURLRequest(test_server().GetURL("/cacheable"));
  429. url_request->SetExtraRequestHeaderByName("if-modified-since", "bar",
  430. /*overwrite=*/true);
  431. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  432. ASSERT_FALSE(
  433. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  434. }
  435. TEST_F(NetworkServiceMemoryCacheTest, CreateWriter_IfNoneMatch) {
  436. std::unique_ptr<net::URLRequest> url_request =
  437. CreateURLRequest(test_server().GetURL("/cacheable"));
  438. url_request->SetExtraRequestHeaderByName("if-none-match", "bar",
  439. /*overwrite=*/true);
  440. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  441. ASSERT_FALSE(
  442. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  443. }
  444. TEST_F(NetworkServiceMemoryCacheTest, CacheControlBogus) {
  445. std::unique_ptr<net::URLRequest> url_request =
  446. CreateURLRequest(test_server().GetURL("/cacheable"));
  447. url_request->SetExtraRequestHeaderByName("cache-control", "bogus",
  448. /*overwrite=*/true);
  449. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  450. ASSERT_TRUE(
  451. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  452. }
  453. TEST_F(NetworkServiceMemoryCacheTest, CacheControlNoCache) {
  454. std::unique_ptr<net::URLRequest> url_request =
  455. CreateURLRequest(test_server().GetURL("/cacheable"));
  456. url_request->SetExtraRequestHeaderByName("cache-control", "no-cache",
  457. /*overwrite=*/true);
  458. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  459. ASSERT_FALSE(
  460. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  461. }
  462. TEST_F(NetworkServiceMemoryCacheTest, PragmaNoCatche) {
  463. std::unique_ptr<net::URLRequest> url_request =
  464. CreateURLRequest(test_server().GetURL("/cacheable"));
  465. url_request->SetExtraRequestHeaderByName("pragma", "no-cache",
  466. /*overwrite=*/true);
  467. mojom::URLResponseHeadPtr response_head = CreateCacheableURLResponseHead();
  468. ASSERT_FALSE(
  469. WriterWillBeCreatedToStoreResponse(url_request.get(), response_head));
  470. }
  471. TEST_F(NetworkServiceMemoryCacheTest, CanServe_Basic) {
  472. ResourceRequest request = CreateRequest("/cacheable");
  473. StoreResponseToMemoryCache(request);
  474. ASSERT_TRUE(CanServeFromMemoryCache(request));
  475. }
  476. TEST_F(NetworkServiceMemoryCacheTest, CanServe_NetworkIsolationKeyIsTransient) {
  477. base::test::ScopedFeatureList feature_list;
  478. feature_list.InitAndEnableFeature(
  479. net::features::kSplitCacheByNetworkIsolationKey);
  480. ResourceRequest request = CreateRequest("/cacheable");
  481. StoreResponseToMemoryCache(request);
  482. ASSERT_TRUE(CanServeFromMemoryCache(request));
  483. ASSERT_FALSE(CanServeFromMemoryCache(
  484. request, net::NetworkIsolationKey::CreateTransient()));
  485. }
  486. TEST_F(NetworkServiceMemoryCacheTest, CanServe_InvalidURL) {
  487. ResourceRequest request;
  488. request.url = GURL();
  489. ASSERT_FALSE(CanServeFromMemoryCache(request));
  490. }
  491. TEST_F(NetworkServiceMemoryCacheTest, CanServe_SchemeIsNeitherHTTPNorHTTPS) {
  492. ResourceRequest request;
  493. request.url = GURL("data:text/plain;foo");
  494. ASSERT_FALSE(CanServeFromMemoryCache(request));
  495. }
  496. TEST_F(NetworkServiceMemoryCacheTest, CanServe_MethodIsNotGet) {
  497. ResourceRequest request = CreateRequest("/cacheable");
  498. request.method = net::HttpRequestHeaders::kPostMethod;
  499. ASSERT_FALSE(CanServeFromMemoryCache(request));
  500. }
  501. TEST_F(NetworkServiceMemoryCacheTest, CanServe_BypassCache) {
  502. ResourceRequest request = CreateRequest("/cacheable");
  503. request.load_flags |= net::LOAD_BYPASS_CACHE;
  504. ASSERT_FALSE(CanServeFromMemoryCache(request));
  505. }
  506. TEST_F(NetworkServiceMemoryCacheTest, CanServe_DisableCache) {
  507. ResourceRequest request = CreateRequest("/cacheable");
  508. request.load_flags |= net::LOAD_DISABLE_CACHE;
  509. ASSERT_FALSE(CanServeFromMemoryCache(request));
  510. }
  511. TEST_F(NetworkServiceMemoryCacheTest, CanServe_ValidateCache) {
  512. ResourceRequest request = CreateRequest("/cacheable");
  513. request.load_flags |= net::LOAD_VALIDATE_CACHE;
  514. ASSERT_FALSE(CanServeFromMemoryCache(request));
  515. }
  516. TEST_F(NetworkServiceMemoryCacheTest, CanServe_BlockedByRequestHeaders) {
  517. constexpr const char* kSpecialHeaders[][2] = {
  518. {"if-Unmodified-since", "foo"},
  519. {"if-mAtch", "foo"},
  520. {"if-raNge", "foo"},
  521. {"if-modiFied-since", "foo"},
  522. {"IF-NONE-MATCH", "foo"},
  523. {"cachE-control", "no-cache"},
  524. {"praGma", "no-cache"},
  525. {"Cache-Control", "max-age=0"},
  526. {"Range", "bytes=0-"},
  527. };
  528. // Store a response to the in-memory cache first.
  529. {
  530. ResourceRequest request = CreateRequest("/cacheable");
  531. StoreResponseToMemoryCache(request);
  532. }
  533. for (const auto& [name, value] : kSpecialHeaders) {
  534. SCOPED_TRACE(base::StringPrintf("header='%s', value='%s'", name, value));
  535. ResourceRequest request = CreateRequest("/cacheable");
  536. request.headers.SetHeader(name, value);
  537. ASSERT_FALSE(CanServeFromMemoryCache(request));
  538. }
  539. }
  540. TEST_F(NetworkServiceMemoryCacheTest, CanServe_Expired) {
  541. const uint64_t kMaxAge = 60;
  542. ResourceRequest request =
  543. CreateRequest(base::StringPrintf("/cacheable?max-age=%" PRId64, kMaxAge));
  544. StoreResponseToMemoryCache(request);
  545. ASSERT_TRUE(CanServeFromMemoryCache(request));
  546. // The response has `max-age=60`. Set the current time 61 seconds later to
  547. // make the cached response stale.
  548. memory_cache().SetCurrentTimeForTesting(base::Time::Now() +
  549. base::Seconds(kMaxAge + 1));
  550. ASSERT_FALSE(CanServeFromMemoryCache(request));
  551. }
  552. TEST_F(NetworkServiceMemoryCacheTest, CanServe_ResponseTooLarge) {
  553. ResourceRequest request = CreateRequest(
  554. base::StringPrintf("/cacheable?body-size=%d", kMaxPerEntrySize + 1));
  555. StoreResponseToMemoryCache(request);
  556. ASSERT_FALSE(CanServeFromMemoryCache(request));
  557. }
  558. TEST_F(NetworkServiceMemoryCacheTest,
  559. CanServe_ResponseTooLargeWithoutContentLength) {
  560. ResourceRequest request = CreateRequest(base::StringPrintf(
  561. "/cacheable_without_content_length?body-size=%d", kMaxPerEntrySize + 1));
  562. StoreResponseToMemoryCache(request);
  563. ASSERT_FALSE(CanServeFromMemoryCache(request));
  564. }
  565. TEST_F(NetworkServiceMemoryCacheTest,
  566. CanServe_SplitCacheByNetworkIsolationKeyEnabled) {
  567. base::test::ScopedFeatureList feature_list;
  568. feature_list.InitAndEnableFeature(
  569. net::features::kSplitCacheByNetworkIsolationKey);
  570. ResourceRequest request = CreateRequest("/cacheable");
  571. StoreResponseToMemoryCache(request);
  572. net::SchemefulSite same_site(request.url);
  573. net::NetworkIsolationKey same_site_network_isolation_key(
  574. /*top_frame_site=*/same_site, /*frame_site=*/same_site);
  575. ASSERT_TRUE(
  576. CanServeFromMemoryCache(request, same_site_network_isolation_key));
  577. net::SchemefulSite other_site(GURL("https://example.test"));
  578. net::NetworkIsolationKey other_site_network_isolation_key(
  579. /*top_frame_site=*/same_site, /*frame_site=*/other_site);
  580. ASSERT_FALSE(
  581. CanServeFromMemoryCache(request, other_site_network_isolation_key));
  582. }
  583. TEST_F(NetworkServiceMemoryCacheTest, CanServe_CorpBlocked) {
  584. ResourceRequest request = CreateRequest("/cacheable");
  585. StoreResponseToMemoryCache(request);
  586. request.mode = mojom::RequestMode::kNoCors;
  587. request.request_initiator =
  588. url::Origin::Create(GURL("https://other-origin.test/"));
  589. net::SchemefulSite site(request.url);
  590. net::NetworkIsolationKey network_isolation_key(/*top_frame_site=*/site,
  591. /*frame_site=*/site);
  592. CrossOriginEmbedderPolicy cross_origin_embedder_policy;
  593. cross_origin_embedder_policy.value =
  594. mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
  595. ASSERT_FALSE(CanServeFromMemoryCache(request, network_isolation_key,
  596. cross_origin_embedder_policy));
  597. }
  598. TEST_F(NetworkServiceMemoryCacheTest, CanServe_CorbBlockedNoSniff) {
  599. ResourceRequest request = CreateRequest("/corb_nosniff");
  600. StoreResponseToMemoryCache(request);
  601. ASSERT_TRUE(CanServeFromMemoryCache(request));
  602. const auto other_origin =
  603. url::Origin::Create(GURL("https://other-origin.test"));
  604. net::SchemefulSite other_site(other_origin);
  605. net::NetworkIsolationKey network_isolation_key(
  606. /*top_frame_site=*/other_site, /*frame_site=*/other_site);
  607. request.request_initiator = other_origin;
  608. ASSERT_FALSE(CanServeFromMemoryCache(request, network_isolation_key));
  609. }
  610. TEST_F(NetworkServiceMemoryCacheTest, CanServe_CorbBlockedSniff) {
  611. ResourceRequest request = CreateRequest("/corb_sniff");
  612. StoreResponseToMemoryCache(request);
  613. ASSERT_TRUE(CanServeFromMemoryCache(request));
  614. const auto other_origin =
  615. url::Origin::Create(GURL("https://other-origin.test"));
  616. net::SchemefulSite other_site(other_origin);
  617. net::NetworkIsolationKey network_isolation_key(
  618. /*top_frame_site=*/other_site, /*frame_site=*/other_site);
  619. request.request_initiator = other_origin;
  620. ASSERT_FALSE(CanServeFromMemoryCache(request, network_isolation_key));
  621. }
  622. TEST_F(NetworkServiceMemoryCacheTest, CanServe_VaryHeaderAcceptEncoding) {
  623. // The `/echoheadercache` handler sends `Vary: foo` header.
  624. ResourceRequest request = CreateRequest("/echoheadercache?Accept-Encoding");
  625. request.headers.SetHeader("accept-encoding", "gzip");
  626. StoreResponseToMemoryCache(request);
  627. ASSERT_TRUE(CanServeFromMemoryCache(request));
  628. // Stored response should not be served when the header that is specified in
  629. // `Vary` has different value.
  630. request.headers.SetHeader("accept-encoding", "br");
  631. ASSERT_FALSE(CanServeFromMemoryCache(request));
  632. // Specifying the net::LOAD_SKIP_VARY_CHECK flag skips Vary header checks.
  633. request.load_flags |= net::LOAD_SKIP_VARY_CHECK;
  634. ASSERT_TRUE(CanServeFromMemoryCache(request));
  635. }
  636. TEST_F(NetworkServiceMemoryCacheTest, CanServe_MultipleVaryHeader) {
  637. ResourceRequest request =
  638. CreateRequest("/echoheadercache?Accept-Encoding,Origin");
  639. request.headers.SetHeader("accept-encoding", "gzip");
  640. request.headers.SetHeader("origin", "https://a.test");
  641. StoreResponseToMemoryCache(request);
  642. ASSERT_TRUE(CanServeFromMemoryCache(request));
  643. request.headers.SetHeader("origin", "https://b.test");
  644. ASSERT_FALSE(CanServeFromMemoryCache(request));
  645. }
  646. // TODO(https://crbug.com/1339708): Change the test name and the expectation
  647. // once we implement appropriate Vary checks.
  648. TEST_F(NetworkServiceMemoryCacheTest, CanServe_UnsupportedVaryHeaderCookie) {
  649. ResourceRequest request = CreateRequest("/echoheadercache?Cookie");
  650. request.headers.SetHeader("cookie", "foo");
  651. StoreResponseToMemoryCache(request);
  652. ASSERT_FALSE(CanServeFromMemoryCache(request));
  653. }
  654. TEST_F(NetworkServiceMemoryCacheTest, CanServe_UnsupportedMultipleVaryHeader) {
  655. ResourceRequest request =
  656. CreateRequest("/echoheadercache?Accept-Encoding,X-Foo");
  657. request.headers.SetHeader("accept-encoding", "gzip");
  658. request.headers.SetHeader("x-foo", "bar");
  659. StoreResponseToMemoryCache(request);
  660. ASSERT_FALSE(CanServeFromMemoryCache(request));
  661. }
  662. TEST_F(NetworkServiceMemoryCacheTest, CanServe_DevToolsAttached) {
  663. ResourceRequest request = CreateRequest("/cacheable?max-age=120");
  664. request.devtools_request_id = "fake-id";
  665. StoreResponseToMemoryCache(request);
  666. MockDevToolsObserver devtools_observer;
  667. request.trusted_params = ResourceRequest::TrustedParams();
  668. request.trusted_params->devtools_observer = devtools_observer.Bind();
  669. LoaderPair pair = CreateLoaderAndStart(request);
  670. pair.client->RunUntilComplete();
  671. const URLLoaderCompletionStatus& status = pair.client->completion_status();
  672. ASSERT_EQ(status.error_code, net::OK);
  673. ASSERT_TRUE(status.exists_in_memory_cache);
  674. devtools_observer.WaitUntilRawResponse(0u);
  675. ASSERT_EQ(200, devtools_observer.raw_response_http_status_code());
  676. // Check whether the cached response has `Cache-Control: max-age=120` as the
  677. // original response had.
  678. bool has_expected_header = false;
  679. for (const auto& pair : devtools_observer.response_headers()) {
  680. if (base::EqualsCaseInsensitiveASCII(pair->key, "cache-control") &&
  681. pair->value == "max-age=120") {
  682. has_expected_header = true;
  683. break;
  684. }
  685. }
  686. ASSERT_TRUE(has_expected_header);
  687. }
  688. TEST_F(NetworkServiceMemoryCacheTest, UpdateStoredCache) {
  689. ResourceRequest request = CreateRequest("/cacheable");
  690. StoreResponseToMemoryCache(request);
  691. net::SchemefulSite site(request.url);
  692. net::NetworkIsolationKey network_isolation_key(/*top_frame_site=*/site,
  693. /*frame_site=*/site);
  694. absl::optional<std::string> cache_key = memory_cache().CanServe(
  695. mojom::kURLLoadOptionNone, request, network_isolation_key,
  696. CrossOriginEmbedderPolicy(),
  697. /*client_security_state=*/nullptr);
  698. ASSERT_TRUE(cache_key.has_value());
  699. mojom::URLResponseHeadPtr response =
  700. memory_cache().GetResponseHeadForTesting(*cache_key);
  701. base::Time first_response_time = response->response_time;
  702. // Store the same response again. Force validation to update the response.
  703. request.load_flags |= net::LOAD_VALIDATE_CACHE;
  704. StoreResponseToMemoryCache(request);
  705. response = memory_cache().GetResponseHeadForTesting(*cache_key);
  706. base::Time second_response_time = response->response_time;
  707. // Compare response time to make sure the stored response is updated.
  708. ASSERT_LT(first_response_time, second_response_time);
  709. }
  710. TEST_F(NetworkServiceMemoryCacheTest, EvictLeastRecentlyUsed) {
  711. constexpr size_t kBodySize = kMaxPerEntrySize;
  712. // Stores two responses to consume the full budget of the in-memory cache.
  713. ResourceRequest request1 = CreateRequest(
  714. base::StringPrintf("/cacheable?id=1&body-size=%zu", kBodySize));
  715. StoreResponseToMemoryCache(request1);
  716. ResourceRequest request2 = CreateRequest(
  717. base::StringPrintf("/cacheable?id=2&body-size=%zu", kBodySize));
  718. StoreResponseToMemoryCache(request2);
  719. ASSERT_TRUE(CanServeFromMemoryCache(request1));
  720. ASSERT_TRUE(CanServeFromMemoryCache(request2));
  721. // Stores the third response. It should evict the first stored response.
  722. ResourceRequest request3 = CreateRequest(
  723. base::StringPrintf("/cacheable?id=3&body-size=%zu", kBodySize));
  724. StoreResponseToMemoryCache(request3);
  725. ASSERT_FALSE(CanServeFromMemoryCache(request1));
  726. ASSERT_TRUE(CanServeFromMemoryCache(request2));
  727. ASSERT_TRUE(CanServeFromMemoryCache(request3));
  728. ASSERT_EQ(memory_cache().total_bytes(), kBodySize * 2);
  729. }
  730. // Tests that a stored response is deleted when a subsequent request that
  731. // bypasses the cache results in a redirect.
  732. TEST_F(NetworkServiceMemoryCacheTest, CachedAfterRedirect) {
  733. ResourceRequest request = CreateRequest("/cacheable_or_redirect");
  734. StoreResponseToMemoryCache(request);
  735. ASSERT_TRUE(CanServeFromMemoryCache(request));
  736. MakeCacheableHandlerSendRedirect();
  737. request.load_flags |= net::LOAD_BYPASS_CACHE;
  738. LoaderPair pair = CreateLoaderAndStart(request);
  739. pair.client->RunUntilRedirectReceived();
  740. pair.loader_remote->FollowRedirect(
  741. /*removed_headers=*/{}, /*modified_headers=*/{},
  742. /*modified_cors_exempt_headers=*/{}, /*new_url=*/absl::nullopt);
  743. pair.client->RunUntilComplete();
  744. request.load_flags &= ~net::LOAD_BYPASS_CACHE;
  745. ASSERT_FALSE(CanServeFromMemoryCache(request));
  746. }
  747. TEST_F(NetworkServiceMemoryCacheTest, Clear) {
  748. constexpr int kBodySize = 64;
  749. // Stores three responses.
  750. ResourceRequest request1 = CreateRequest(
  751. base::StringPrintf("/cacheable?id=1&body-size=%d", kBodySize));
  752. StoreResponseToMemoryCache(request1);
  753. ResourceRequest request2 = CreateRequest(
  754. base::StringPrintf("/cacheable?id=2&body-size=%d", kBodySize));
  755. StoreResponseToMemoryCache(request2);
  756. ResourceRequest request3 = CreateRequest(
  757. base::StringPrintf("/cacheable?id=3&body-size=%d", kBodySize));
  758. StoreResponseToMemoryCache(request3);
  759. ASSERT_TRUE(CanServeFromMemoryCache(request1));
  760. ASSERT_TRUE(CanServeFromMemoryCache(request2));
  761. ASSERT_TRUE(CanServeFromMemoryCache(request3));
  762. memory_cache().Clear();
  763. ASSERT_EQ(memory_cache().total_bytes(), 0u);
  764. ASSERT_FALSE(CanServeFromMemoryCache(request1));
  765. ASSERT_FALSE(CanServeFromMemoryCache(request2));
  766. ASSERT_FALSE(CanServeFromMemoryCache(request3));
  767. }
  768. TEST_F(NetworkServiceMemoryCacheTest, ClearOnMemoryPressure) {
  769. ResourceRequest request = CreateRequest("/cacheable");
  770. StoreResponseToMemoryCache(request);
  771. ASSERT_TRUE(CanServeFromMemoryCache(request));
  772. base::MemoryPressureListener::NotifyMemoryPressure(
  773. base::MemoryPressureListener::MemoryPressureLevel::
  774. MEMORY_PRESSURE_LEVEL_CRITICAL);
  775. task_environment().RunUntilIdle();
  776. ASSERT_EQ(memory_cache().total_bytes(), 0u);
  777. ASSERT_FALSE(CanServeFromMemoryCache(request));
  778. }
  779. TEST_F(NetworkServiceMemoryCacheTest, ClientDisconnectedWhileCaching) {
  780. ResourceRequest request = CreateRequest("/cacheable");
  781. LoaderPair pair = CreateLoaderAndStart(request);
  782. pair.client->RunUntilResponseReceived();
  783. pair.client->Unbind();
  784. ASSERT_TRUE(pair.loader_remote.is_connected());
  785. base::RunLoop loop;
  786. pair.loader_remote.set_disconnect_handler(loop.QuitClosure());
  787. loop.Run();
  788. ASSERT_FALSE(CanServeFromMemoryCache(request));
  789. }
  790. TEST_F(NetworkServiceMemoryCacheTest, ServeFromCache_Basic) {
  791. constexpr int kBodySize = 371;
  792. const std::string kExpectedBody(kBodySize, 'a');
  793. ResourceRequest request =
  794. CreateRequest(base::StringPrintf("/cacheable?body-size=%d", kBodySize));
  795. StoreResponseToMemoryCache(request);
  796. const base::TimeTicks before_start = base::TimeTicks::Now();
  797. LoaderPair pair = CreateLoaderAndStart(request);
  798. pair.client->RunUntilComplete();
  799. const URLLoaderCompletionStatus& status = pair.client->completion_status();
  800. ASSERT_EQ(status.error_code, net::OK);
  801. ASSERT_TRUE(status.exists_in_memory_cache);
  802. const mojom::URLResponseHeadPtr& response = pair.client->response_head();
  803. ASSERT_FALSE(response->network_accessed);
  804. ASSERT_FALSE(response->is_validated);
  805. ASSERT_TRUE(response->was_fetched_via_cache);
  806. ASSERT_LT(before_start, response->request_start);
  807. ASSERT_LT(before_start, response->response_start);
  808. const net::LoadTimingInfo& load_timing = response->load_timing;
  809. ASSERT_LT(before_start, load_timing.request_start);
  810. ASSERT_LT(before_start, load_timing.send_start);
  811. ASSERT_LT(before_start, load_timing.send_end);
  812. ASSERT_LT(before_start, load_timing.receive_headers_start);
  813. ASSERT_LT(before_start, load_timing.receive_headers_end);
  814. std::string received_body;
  815. ASSERT_TRUE(mojo::BlockingCopyToString(pair.client->response_body_release(),
  816. &received_body));
  817. ASSERT_EQ(kExpectedBody, received_body);
  818. }
  819. TEST_F(NetworkServiceMemoryCacheTest, ServeFromCache_DisableLoadTiming) {
  820. ResourceRequest request = CreateRequest("/cacheable");
  821. StoreResponseToMemoryCache(request);
  822. const base::TimeTicks before_start = base::TimeTicks::Now();
  823. request.enable_load_timing = false;
  824. LoaderPair pair = CreateLoaderAndStart(request);
  825. pair.client->RunUntilComplete();
  826. const URLLoaderCompletionStatus& status = pair.client->completion_status();
  827. ASSERT_EQ(status.error_code, net::OK);
  828. ASSERT_TRUE(status.exists_in_memory_cache);
  829. const mojom::URLResponseHeadPtr& response = pair.client->response_head();
  830. ASSERT_FALSE(response->network_accessed);
  831. ASSERT_FALSE(response->is_validated);
  832. ASSERT_TRUE(response->was_fetched_via_cache);
  833. ASSERT_LT(before_start, response->request_start);
  834. ASSERT_LT(before_start, response->response_start);
  835. const net::LoadTimingInfo& load_timing = response->load_timing;
  836. ASSERT_TRUE(load_timing.request_start.is_null());
  837. ASSERT_TRUE(load_timing.send_start.is_null());
  838. ASSERT_TRUE(load_timing.send_end.is_null());
  839. ASSERT_TRUE(load_timing.receive_headers_start.is_null());
  840. ASSERT_TRUE(load_timing.receive_headers_end.is_null());
  841. }
  842. TEST_F(NetworkServiceMemoryCacheTest, ServeFromCache_LargeBody) {
  843. constexpr uint32_t kReadDataSize = 512;
  844. // Arbitrary response body size larger than `kReadDataSize`.
  845. constexpr int kBodySize = 2 * 1024 + 659;
  846. DCHECK_GE(kMaxPerEntrySize, kBodySize);
  847. ResourceRequest request =
  848. CreateRequest(base::StringPrintf("/cacheable?body-size=%d", kBodySize));
  849. StoreResponseToMemoryCache(request);
  850. LoaderPair pair = CreateLoaderAndStart(request);
  851. pair.client->RunUntilResponseReceived();
  852. mojo::ScopedDataPipeConsumerHandle consumer_handle =
  853. pair.client->response_body_release();
  854. std::string received_body;
  855. while (true) {
  856. char buf[kReadDataSize];
  857. uint32_t num_bytes = kReadDataSize;
  858. MojoResult result =
  859. consumer_handle->ReadData(buf, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
  860. if (result == MOJO_RESULT_SHOULD_WAIT) {
  861. base::RunLoop run_loop;
  862. base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
  863. run_loop.QuitClosure());
  864. run_loop.Run();
  865. continue;
  866. }
  867. if (result == MOJO_RESULT_FAILED_PRECONDITION)
  868. break;
  869. ASSERT_EQ(result, MOJO_RESULT_OK);
  870. received_body.append(buf, num_bytes);
  871. }
  872. pair.client->RunUntilComplete();
  873. const URLLoaderCompletionStatus& status = pair.client->completion_status();
  874. ASSERT_EQ(status.error_code, net::OK);
  875. ASSERT_TRUE(status.exists_in_memory_cache);
  876. const std::string kExpectedBody(kBodySize, 'a');
  877. ASSERT_EQ(kExpectedBody, received_body);
  878. }
  879. TEST_F(NetworkServiceMemoryCacheTest,
  880. ServeFromCache_DataPipeDisconnectWhileReading) {
  881. constexpr int kBodySize = 512;
  882. constexpr int kReadDataSize = kBodySize / 2;
  883. ResourceRequest request =
  884. CreateRequest(base::StringPrintf("/cacheable?body-size=%d", kBodySize));
  885. StoreResponseToMemoryCache(request);
  886. // Set a small data pipe capacity so that writing data to a data pipe doesn't
  887. // complete at once.
  888. memory_cache().SetDataPipeCapacityForTesting(kReadDataSize);
  889. LoaderPair pair = CreateLoaderAndStart(request);
  890. pair.client->RunUntilResponseReceived();
  891. mojo::ScopedDataPipeConsumerHandle consumer_handle =
  892. pair.client->response_body_release();
  893. // Read the half of the response body.
  894. int num_read = 0;
  895. while (num_read < kReadDataSize) {
  896. char buf[kReadDataSize];
  897. uint32_t num_bytes = kReadDataSize;
  898. MojoResult result =
  899. consumer_handle->ReadData(buf, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
  900. if (result == MOJO_RESULT_SHOULD_WAIT) {
  901. base::RunLoop run_loop;
  902. base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
  903. run_loop.QuitClosure());
  904. run_loop.Run();
  905. continue;
  906. }
  907. ASSERT_EQ(result, MOJO_RESULT_OK);
  908. num_read += num_bytes;
  909. }
  910. consumer_handle.reset();
  911. pair.client->RunUntilComplete();
  912. ASSERT_EQ(pair.client->completion_status().error_code, net::ERR_FAILED);
  913. }
  914. TEST_F(NetworkServiceMemoryCacheTest, ServeFromCache_GzipResponse) {
  915. constexpr int64_t kBodySize = 100;
  916. const std::string kContent(kBodySize, 'x');
  917. ResourceRequest request =
  918. CreateRequest(base::StringPrintf("/gzip-body?%s", kContent.c_str()));
  919. StoreResponseToMemoryCache(request);
  920. ASSERT_TRUE(CanServeFromMemoryCache(request));
  921. LoaderPair pair = CreateLoaderAndStart(request);
  922. pair.client->RunUntilComplete();
  923. const URLLoaderCompletionStatus& status = pair.client->completion_status();
  924. ASSERT_EQ(status.error_code, net::OK);
  925. ASSERT_EQ(status.decoded_body_length, kBodySize);
  926. ASSERT_LT(status.encoded_body_length, kBodySize);
  927. }
  928. TEST_F(NetworkServiceMemoryCacheTest, InvalidateOnNonSafeMethod) {
  929. ResourceRequest request = CreateRequest("/cacheable");
  930. StoreResponseToMemoryCache(request);
  931. ASSERT_TRUE(CanServeFromMemoryCache(request));
  932. ResourceRequest request2 = CreateRequest("/cacheable");
  933. request2.method = "PUT";
  934. LoaderPair pair = CreateLoaderAndStart(request2);
  935. pair.client->RunUntilResponseReceived();
  936. ASSERT_FALSE(CanServeFromMemoryCache(request));
  937. }
  938. TEST_F(NetworkServiceMemoryCacheWithFactoryOverrideTest,
  939. MemoryCacheShouldNotBeUsed) {
  940. ResourceRequest request = CreateRequest(base::StringPrintf("/cacheable"));
  941. StoreResponseToMemoryCache(request);
  942. ASSERT_FALSE(CanServeFromMemoryCache(request));
  943. }
  944. } // namespace network