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