1 // Copyright 2018 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
5 #include <memory>
6 #include <string>
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/files/file_util.h"
11 #include "base/location.h"
12 #include "base/run_loop.h"
13 #include "base/stl_util.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "base/test/task_environment.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "mojo/public/cpp/bindings/remote.h"
18 #include "net/base/cache_type.h"
19 #include "net/base/features.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/test_completion_callback.h"
22 #include "net/disk_cache/disk_cache.h"
23 #include "net/disk_cache/disk_cache_test_util.h"
24 #include "net/http/http_cache.h"
25 #include "net/http/http_network_session.h"
26 #include "net/http/http_server_properties_manager.h"
27 #include "net/http/http_transaction_factory.h"
28 #include "net/url_request/url_request_context.h"
29 #include "net/url_request/url_request_context_builder.h"
30 #include "services/network/network_context.h"
31 #include "services/network/network_service.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 namespace network {
35
36 namespace {
37
38 struct CacheTestEntry {
39 const char* url;
40 const char* date;
41 };
42
43 HttpCacheDataRemover::HttpCacheDataRemoverCallback
MakeHttpCacheDataRemoverCallback(base::OnceClosure callback)44 MakeHttpCacheDataRemoverCallback(base::OnceClosure callback) {
45 return base::BindOnce(
46 [](base::OnceClosure callback, HttpCacheDataRemover* remover) {
47 std::move(callback).Run();
48 },
49 std::move(callback));
50 }
51
52 constexpr CacheTestEntry kCacheEntries[] = {
53 {"http://www.google.com", "15 Jun 1975"},
54 {"https://www.google.com", "15 Jun 1985"},
55 {"http://www.wikipedia.com", "15 Jun 1995"},
56 {"https://www.wikipedia.com", "15 Jun 2005"},
57 {"http://localhost:1234/mysite", "15 Jun 2015"},
58 {"https://localhost:1234/mysite", "15 Jun 2016"},
59 {"http://localhost:3456/yoursite", "15 Jun 2017"},
60 {"https://localhost:3456/yoursite", "15 Jun 2018"}};
61
CreateContextParams()62 mojom::NetworkContextParamsPtr CreateContextParams() {
63 mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New();
64 // Use a fixed proxy config, to avoid dependencies on local network
65 // configuration.
66 params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect();
67 return params;
68 }
69
70 class HttpCacheDataRemoverTest : public testing::Test {
71 public:
HttpCacheDataRemoverTest()72 HttpCacheDataRemoverTest()
73 : task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
74 network_service_(NetworkService::CreateForTesting()) {}
75
76 ~HttpCacheDataRemoverTest() override = default;
77
SetUp()78 void SetUp() override {
79 InitNetworkContext();
80
81 cache_ = network_context_->url_request_context()
82 ->http_transaction_factory()
83 ->GetCache();
84 ASSERT_TRUE(cache_);
85 {
86 net::TestCompletionCallback callback;
87 int rv = cache_->GetBackend(&backend_, callback.callback());
88 ASSERT_EQ(net::OK, callback.GetResult(rv));
89 ASSERT_TRUE(backend_);
90 }
91
92 // Create some entries in the cache.
93 for (const CacheTestEntry& test_entry : kCacheEntries) {
94 TestEntryResultCompletionCallback callback;
95 std::string key = ComputeCacheKey(test_entry.url);
96 disk_cache::EntryResult result =
97 backend_->CreateEntry(key, net::HIGHEST, callback.callback());
98 result = callback.GetResult(std::move(result));
99 ASSERT_EQ(net::OK, result.net_error());
100 disk_cache::Entry* entry = result.ReleaseEntry();
101 ASSERT_TRUE(entry);
102 base::Time time;
103 ASSERT_TRUE(base::Time::FromString(test_entry.date, &time));
104 entry->SetLastUsedTimeForTest(time);
105 entry->Close();
106 task_environment_.RunUntilIdle();
107 }
108 ASSERT_EQ(base::size(kCacheEntries),
109 static_cast<size_t>(backend_->GetEntryCount()));
110 }
111
ComputeCacheKey(const std::string & url_string)112 std::string ComputeCacheKey(const std::string& url_string) {
113 GURL url(url_string);
114 const auto kOrigin = url::Origin::Create(url);
115 net::HttpRequestInfo request_info;
116 request_info.url = url;
117 request_info.method = "GET";
118 request_info.network_isolation_key =
119 net::NetworkIsolationKey(kOrigin, kOrigin);
120 return cache_->GenerateCacheKeyForTest(&request_info);
121 }
122
RemoveData(mojom::ClearDataFilterPtr filter,base::Time start_time,base::Time end_time)123 void RemoveData(mojom::ClearDataFilterPtr filter,
124 base::Time start_time,
125 base::Time end_time) {
126 base::RunLoop run_loop;
127 std::unique_ptr<HttpCacheDataRemover> data_remover =
128 HttpCacheDataRemover::CreateAndStart(
129 network_context_->url_request_context(), std::move(filter),
130 start_time, end_time,
131 MakeHttpCacheDataRemoverCallback(run_loop.QuitClosure()));
132 run_loop.Run();
133 }
134
HasEntry(const std::string & url_string)135 bool HasEntry(const std::string& url_string) {
136 std::string key = ComputeCacheKey(url_string);
137 base::RunLoop run_loop;
138 TestEntryResultCompletionCallback callback;
139 disk_cache::EntryResult result =
140 backend_->OpenEntry(key, net::HIGHEST, callback.callback());
141 if (result.net_error() == net::ERR_IO_PENDING)
142 result = callback.WaitForResult();
143 disk_cache::Entry* entry = result.ReleaseEntry();
144 if (entry)
145 entry->Close();
146 return entry != nullptr;
147 }
148
149 protected:
InitNetworkContext()150 void InitNetworkContext() {
151 mojom::NetworkContextParamsPtr context_params = CreateContextParams();
152 context_params->http_cache_enabled = true;
153 network_context_remote_.reset();
154 network_context_ = std::make_unique<NetworkContext>(
155 network_service_.get(),
156 network_context_remote_.BindNewPipeAndPassReceiver(),
157 std::move(context_params));
158 }
159
160 base::test::TaskEnvironment task_environment_;
161 std::unique_ptr<NetworkService> network_service_;
162 std::unique_ptr<NetworkContext> network_context_;
163
164 // Stores the mojo::Remote<NetworkContext> of the most recently created
165 // NetworkContext.
166 mojo::Remote<mojom::NetworkContext> network_context_remote_;
167 disk_cache::Backend* backend_ = nullptr;
168
169 private:
170 net::HttpCache* cache_;
171 };
172
173 class HttpCacheDataRemoverSplitCacheTest : public HttpCacheDataRemoverTest {
174 protected:
SetUp()175 void SetUp() override {
176 feature_list_.InitAndEnableFeature(
177 net::features::kSplitCacheByNetworkIsolationKey);
178 HttpCacheDataRemoverTest::SetUp();
179 }
180
181 private:
182 base::test::ScopedFeatureList feature_list_;
183 };
184
TEST_F(HttpCacheDataRemoverTest,ClearAll)185 TEST_F(HttpCacheDataRemoverTest, ClearAll) {
186 EXPECT_NE(0, backend_->GetEntryCount());
187 RemoveData(/*url_filter=*/nullptr, base::Time(), base::Time());
188 EXPECT_EQ(0, backend_->GetEntryCount());
189 }
190
TEST_F(HttpCacheDataRemoverTest,FilterDeleteByDomain)191 TEST_F(HttpCacheDataRemoverTest, FilterDeleteByDomain) {
192 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
193 filter->type = mojom::ClearDataFilter_Type::DELETE_MATCHES;
194 filter->domains.push_back("wikipedia.com");
195 filter->domains.push_back("google.com");
196 RemoveData(std::move(filter), base::Time(), base::Time());
197 EXPECT_FALSE(HasEntry(kCacheEntries[0].url));
198 EXPECT_FALSE(HasEntry(kCacheEntries[1].url));
199 EXPECT_FALSE(HasEntry(kCacheEntries[2].url));
200 EXPECT_FALSE(HasEntry(kCacheEntries[3].url));
201 EXPECT_EQ(4, backend_->GetEntryCount());
202 }
203
TEST_F(HttpCacheDataRemoverTest,FilterKeepByDomain)204 TEST_F(HttpCacheDataRemoverTest, FilterKeepByDomain) {
205 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
206 filter->type = mojom::ClearDataFilter_Type::KEEP_MATCHES;
207 filter->domains.push_back("wikipedia.com");
208 filter->domains.push_back("google.com");
209 RemoveData(std::move(filter), base::Time(), base::Time());
210 EXPECT_TRUE(HasEntry(kCacheEntries[0].url));
211 EXPECT_TRUE(HasEntry(kCacheEntries[1].url));
212 EXPECT_TRUE(HasEntry(kCacheEntries[2].url));
213 EXPECT_TRUE(HasEntry(kCacheEntries[3].url));
214 EXPECT_EQ(4, backend_->GetEntryCount());
215 }
216
TEST_F(HttpCacheDataRemoverTest,FilterDeleteByOrigin)217 TEST_F(HttpCacheDataRemoverTest, FilterDeleteByOrigin) {
218 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
219 filter->type = mojom::ClearDataFilter_Type::DELETE_MATCHES;
220 filter->origins.push_back(url::Origin::Create(GURL("http://www.google.com")));
221 filter->origins.push_back(url::Origin::Create(GURL("http://localhost:1234")));
222 RemoveData(std::move(filter), base::Time(), base::Time());
223 EXPECT_FALSE(HasEntry(kCacheEntries[0].url));
224 EXPECT_FALSE(HasEntry(kCacheEntries[4].url));
225 EXPECT_EQ(6, backend_->GetEntryCount());
226 }
227
TEST_F(HttpCacheDataRemoverTest,FilterKeepByOrigin)228 TEST_F(HttpCacheDataRemoverTest, FilterKeepByOrigin) {
229 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
230 filter->type = mojom::ClearDataFilter_Type::KEEP_MATCHES;
231 filter->origins.push_back(url::Origin::Create(GURL("http://www.google.com")));
232 filter->origins.push_back(url::Origin::Create(GURL("http://localhost:1234")));
233 RemoveData(std::move(filter), base::Time(), base::Time());
234 EXPECT_TRUE(HasEntry(kCacheEntries[0].url));
235 EXPECT_TRUE(HasEntry(kCacheEntries[4].url));
236 EXPECT_EQ(2, backend_->GetEntryCount());
237 }
238
TEST_F(HttpCacheDataRemoverTest,FilterDeleteByDomainAndOrigin)239 TEST_F(HttpCacheDataRemoverTest, FilterDeleteByDomainAndOrigin) {
240 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
241 filter->type = mojom::ClearDataFilter_Type::DELETE_MATCHES;
242 filter->domains.push_back("wikipedia.com");
243 filter->origins.push_back(url::Origin::Create(GURL("http://localhost:1234")));
244 RemoveData(std::move(filter), base::Time(), base::Time());
245 EXPECT_FALSE(HasEntry(kCacheEntries[2].url));
246 EXPECT_FALSE(HasEntry(kCacheEntries[3].url));
247 EXPECT_FALSE(HasEntry(kCacheEntries[4].url));
248 EXPECT_EQ(5, backend_->GetEntryCount());
249 }
250
TEST_F(HttpCacheDataRemoverTest,FilterKeepByDomainAndOrigin)251 TEST_F(HttpCacheDataRemoverTest, FilterKeepByDomainAndOrigin) {
252 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
253 filter->type = mojom::ClearDataFilter_Type::KEEP_MATCHES;
254 filter->domains.push_back("wikipedia.com");
255 filter->origins.push_back(url::Origin::Create(GURL("http://localhost:1234")));
256 RemoveData(std::move(filter), base::Time(), base::Time());
257 EXPECT_TRUE(HasEntry(kCacheEntries[2].url));
258 EXPECT_TRUE(HasEntry(kCacheEntries[3].url));
259 EXPECT_TRUE(HasEntry(kCacheEntries[4].url));
260 EXPECT_EQ(3, backend_->GetEntryCount());
261 }
262
TEST_F(HttpCacheDataRemoverTest,FilterByDateFromUnbounded)263 TEST_F(HttpCacheDataRemoverTest, FilterByDateFromUnbounded) {
264 base::Time end_time;
265 ASSERT_TRUE(base::Time::FromString(kCacheEntries[5].date, &end_time));
266 RemoveData(/*filter=*/nullptr, base::Time(), end_time);
267 EXPECT_TRUE(HasEntry(kCacheEntries[5].url));
268 EXPECT_TRUE(HasEntry(kCacheEntries[6].url));
269 EXPECT_TRUE(HasEntry(kCacheEntries[7].url));
270 EXPECT_EQ(3, backend_->GetEntryCount());
271 }
272
TEST_F(HttpCacheDataRemoverTest,FilterByDateToUnbounded)273 TEST_F(HttpCacheDataRemoverTest, FilterByDateToUnbounded) {
274 base::Time start_time;
275 ASSERT_TRUE(base::Time::FromString(kCacheEntries[5].date, &start_time));
276 RemoveData(/*filter=*/nullptr, start_time, base::Time());
277 EXPECT_FALSE(HasEntry(kCacheEntries[5].url));
278 EXPECT_FALSE(HasEntry(kCacheEntries[6].url));
279 EXPECT_FALSE(HasEntry(kCacheEntries[7].url));
280 EXPECT_EQ(5, backend_->GetEntryCount());
281 }
282
TEST_F(HttpCacheDataRemoverTest,FilterByDateRange)283 TEST_F(HttpCacheDataRemoverTest, FilterByDateRange) {
284 base::Time start_time;
285 ASSERT_TRUE(base::Time::FromString(kCacheEntries[1].date, &start_time));
286 base::Time end_time;
287 ASSERT_TRUE(base::Time::FromString(kCacheEntries[6].date, &end_time));
288 RemoveData(/*filter=*/nullptr, start_time, end_time);
289 EXPECT_FALSE(HasEntry(kCacheEntries[1].url));
290 EXPECT_FALSE(HasEntry(kCacheEntries[2].url));
291 EXPECT_FALSE(HasEntry(kCacheEntries[3].url));
292 EXPECT_FALSE(HasEntry(kCacheEntries[4].url));
293 EXPECT_FALSE(HasEntry(kCacheEntries[5].url));
294 EXPECT_EQ(3, backend_->GetEntryCount());
295 }
296
TEST_F(HttpCacheDataRemoverTest,FilterDeleteByDomainAndDate)297 TEST_F(HttpCacheDataRemoverTest, FilterDeleteByDomainAndDate) {
298 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
299 filter->type = mojom::ClearDataFilter_Type::DELETE_MATCHES;
300 filter->domains.push_back("google.com");
301 filter->domains.push_back("wikipedia.com");
302
303 base::Time start_time;
304 ASSERT_TRUE(base::Time::FromString(kCacheEntries[1].date, &start_time));
305 base::Time end_time;
306 ASSERT_TRUE(base::Time::FromString(kCacheEntries[6].date, &end_time));
307
308 RemoveData(std::move(filter), start_time, end_time);
309 EXPECT_FALSE(HasEntry(kCacheEntries[1].url));
310 EXPECT_FALSE(HasEntry(kCacheEntries[2].url));
311 EXPECT_FALSE(HasEntry(kCacheEntries[3].url));
312 EXPECT_EQ(5, backend_->GetEntryCount());
313 }
314
TEST_F(HttpCacheDataRemoverTest,FilterKeepByDomainAndDate)315 TEST_F(HttpCacheDataRemoverTest, FilterKeepByDomainAndDate) {
316 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
317 filter->type = mojom::ClearDataFilter_Type::KEEP_MATCHES;
318 filter->domains.push_back("google.com");
319 filter->domains.push_back("wikipedia.com");
320
321 base::Time start_time;
322 ASSERT_TRUE(base::Time::FromString(kCacheEntries[1].date, &start_time));
323 base::Time end_time;
324 ASSERT_TRUE(base::Time::FromString(kCacheEntries[6].date, &end_time));
325
326 RemoveData(std::move(filter), start_time, end_time);
327 EXPECT_FALSE(HasEntry(kCacheEntries[4].url));
328 EXPECT_FALSE(HasEntry(kCacheEntries[5].url));
329 EXPECT_EQ(6, backend_->GetEntryCount());
330 }
331
TEST_F(HttpCacheDataRemoverTest,DeleteHttpRemover)332 TEST_F(HttpCacheDataRemoverTest, DeleteHttpRemover) {
333 bool callback_invoked = false;
334 std::unique_ptr<HttpCacheDataRemover> data_remover =
335 HttpCacheDataRemover::CreateAndStart(
336 network_context_->url_request_context(),
337 /*url_filter=*/nullptr, base::Time(), base::Time(),
338 MakeHttpCacheDataRemoverCallback(base::BindOnce(
339 [](bool* callback_invoked) { *callback_invoked = true; },
340 &callback_invoked)));
341 // Delete the data remover and make sure after all task have been processed
342 // that the callback wasn't invoked.
343 data_remover.reset();
344 task_environment_.RunUntilIdle();
345 EXPECT_FALSE(callback_invoked);
346 }
347
348 // This test exercises the different code paths wrt whether the cache's backend
349 // is available when clearing the cache.
TEST_F(HttpCacheDataRemoverTest,TestDelayedBackend)350 TEST_F(HttpCacheDataRemoverTest, TestDelayedBackend) {
351 // Reinit the network context without retrieving the cache's backend so the
352 // call to clear the cache does it.
353 InitNetworkContext();
354
355 net::HttpCache* http_cache = network_context_->url_request_context()
356 ->http_transaction_factory()
357 ->GetCache();
358 ASSERT_TRUE(http_cache);
359 ASSERT_FALSE(http_cache->GetCurrentBackend());
360 RemoveData(/*filter=*/nullptr, base::Time(), base::Time());
361
362 // This should have retrieved the backend of the cache.
363 EXPECT_TRUE(http_cache->GetCurrentBackend());
364 // Clear again the cache to test that it works when the backend is readily
365 // available.
366 RemoveData(/*filter=*/nullptr, base::Time(), base::Time());
367 }
368
TEST_F(HttpCacheDataRemoverSplitCacheTest,FilterDeleteByDomain)369 TEST_F(HttpCacheDataRemoverSplitCacheTest, FilterDeleteByDomain) {
370 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
371 filter->type = mojom::ClearDataFilter_Type::DELETE_MATCHES;
372 filter->domains.push_back("wikipedia.com");
373 filter->domains.push_back("google.com");
374 RemoveData(std::move(filter), base::Time(), base::Time());
375 EXPECT_FALSE(HasEntry(kCacheEntries[0].url));
376 EXPECT_FALSE(HasEntry(kCacheEntries[1].url));
377 EXPECT_FALSE(HasEntry(kCacheEntries[2].url));
378 EXPECT_FALSE(HasEntry(kCacheEntries[3].url));
379 EXPECT_EQ(4, backend_->GetEntryCount());
380 }
381
TEST_F(HttpCacheDataRemoverSplitCacheTest,FilterKeepByDomain)382 TEST_F(HttpCacheDataRemoverSplitCacheTest, FilterKeepByDomain) {
383 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
384 filter->type = mojom::ClearDataFilter_Type::KEEP_MATCHES;
385 filter->domains.push_back("wikipedia.com");
386 filter->domains.push_back("google.com");
387 RemoveData(std::move(filter), base::Time(), base::Time());
388 EXPECT_TRUE(HasEntry(kCacheEntries[0].url));
389 EXPECT_TRUE(HasEntry(kCacheEntries[1].url));
390 EXPECT_TRUE(HasEntry(kCacheEntries[2].url));
391 EXPECT_TRUE(HasEntry(kCacheEntries[3].url));
392 EXPECT_EQ(4, backend_->GetEntryCount());
393 }
394
TEST_F(HttpCacheDataRemoverSplitCacheTest,FilterDeleteByOrigin)395 TEST_F(HttpCacheDataRemoverSplitCacheTest, FilterDeleteByOrigin) {
396 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
397 filter->type = mojom::ClearDataFilter_Type::DELETE_MATCHES;
398 filter->origins.push_back(url::Origin::Create(GURL("http://www.google.com")));
399 filter->origins.push_back(url::Origin::Create(GURL("http://localhost:1234")));
400 RemoveData(std::move(filter), base::Time(), base::Time());
401 EXPECT_FALSE(HasEntry(kCacheEntries[0].url));
402 EXPECT_FALSE(HasEntry(kCacheEntries[4].url));
403 EXPECT_EQ(6, backend_->GetEntryCount());
404 }
405
TEST_F(HttpCacheDataRemoverSplitCacheTest,FilterKeepByOrigin)406 TEST_F(HttpCacheDataRemoverSplitCacheTest, FilterKeepByOrigin) {
407 mojom::ClearDataFilterPtr filter = mojom::ClearDataFilter::New();
408 filter->type = mojom::ClearDataFilter_Type::KEEP_MATCHES;
409 filter->origins.push_back(url::Origin::Create(GURL("http://www.google.com")));
410 filter->origins.push_back(url::Origin::Create(GURL("http://localhost:1234")));
411 RemoveData(std::move(filter), base::Time(), base::Time());
412 EXPECT_TRUE(HasEntry(kCacheEntries[0].url));
413 EXPECT_TRUE(HasEntry(kCacheEntries[4].url));
414 EXPECT_EQ(2, backend_->GetEntryCount());
415 }
416
417 } // namespace
418
419 } // namespace network
420