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 "services/network/http_cache_data_counter.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/bind.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/run_loop.h"
16 #include "base/stl_util.h"
17 #include "base/test/bind.h"
18 #include "base/test/task_environment.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "mojo/public/cpp/bindings/remote.h"
21 #include "net/base/cache_type.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/test_completion_callback.h"
24 #include "net/disk_cache/disk_cache.h"
25 #include "net/disk_cache/disk_cache_test_util.h"
26 #include "net/http/http_cache.h"
27 #include "net/http/http_network_session.h"
28 #include "net/http/http_server_properties_manager.h"
29 #include "net/http/http_transaction_factory.h"
30 #include "net/url_request/url_request_context.h"
31 #include "services/network/network_context.h"
32 #include "services/network/network_service.h"
33 #include "services/network/test/fake_test_cert_verifier_params_factory.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 
36 namespace network {
37 
38 namespace {
39 
40 struct CacheTestEntry {
41   const char* url;
42   const char* date;
43   int size;
44 };
45 
46 constexpr CacheTestEntry kCacheEntries[] = {
47     {"http://www.google.com", "15 Jun 1975", 1024},
48     {"https://www.google.com", "15 Jun 1985", 2048},
49     {"http://www.wikipedia.com", "15 Jun 1995", 4096},
50     {"https://www.wikipedia.com", "15 Jun 2005", 8192},
51     {"http://localhost:1234/mysite", "15 Jun 2015", 16384},
52     {"https://localhost:1234/mysite", "15 Jun 2016", 32768},
53     {"http://localhost:3456/yoursite", "15 Jun 2017", 65536},
54     {"https://localhost:3456/yoursite", "15 Jun 2018", 512}};
55 
CreateContextParams()56 mojom::NetworkContextParamsPtr CreateContextParams() {
57   mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New();
58   // Use a dummy CertVerifier that always passes cert verification, since
59   // these unittests don't need to test CertVerifier behavior.
60   params->cert_verifier_params =
61       FakeTestCertVerifierParamsFactory::GetCertVerifierParams();
62   // Use a fixed proxy config, to avoid dependencies on local network
63   // configuration.
64   params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect();
65   return params;
66 }
67 
68 class HttpCacheDataCounterTest : public testing::Test {
69  public:
HttpCacheDataCounterTest()70   HttpCacheDataCounterTest()
71       : task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
72         network_service_(NetworkService::CreateForTesting()) {}
73 
74   ~HttpCacheDataCounterTest() override = default;
75 
SetUp()76   void SetUp() override {
77     ASSERT_TRUE(cache_dir_.CreateUniqueTempDir());
78     InitNetworkContext();
79 
80     net::HttpCache* cache = network_context_->url_request_context()
81                                 ->http_transaction_factory()
82                                 ->GetCache();
83     ASSERT_TRUE(cache);
84     {
85       net::TestCompletionCallback callback;
86       int rv = cache->GetBackend(&backend_, callback.callback());
87       ASSERT_EQ(net::OK, callback.GetResult(rv));
88       ASSERT_TRUE(backend_);
89     }
90 
91     // Create some entries in the cache.
92     for (const CacheTestEntry& test_entry : kCacheEntries) {
93       TestEntryResultCompletionCallback create_entry_callback;
94       disk_cache::EntryResult result = backend_->CreateEntry(
95           test_entry.url, net::HIGHEST, create_entry_callback.callback());
96       result = create_entry_callback.GetResult(std::move(result));
97       ASSERT_EQ(net::OK, result.net_error());
98       disk_cache::Entry* entry = result.ReleaseEntry();
99       ASSERT_TRUE(entry);
100 
101       auto io_buf = base::MakeRefCounted<net::IOBuffer>(test_entry.size);
102       std::fill(io_buf->data(), io_buf->data() + test_entry.size, 0);
103 
104       net::TestCompletionCallback write_data_callback;
105       int rv = entry->WriteData(1, 0, io_buf.get(), test_entry.size,
106                                 write_data_callback.callback(), true);
107       ASSERT_EQ(static_cast<int>(test_entry.size),
108                 write_data_callback.GetResult(rv));
109 
110       base::Time time;
111       ASSERT_TRUE(base::Time::FromString(test_entry.date, &time));
112       entry->SetLastUsedTimeForTest(time);
113       entry->Close();
114       task_environment_.RunUntilIdle();
115     }
116   }
117 
ExpectClose(int actual,int expected)118   void ExpectClose(int actual, int expected) {
119     int margin = std::max(256, expected / 5);
120     int expected_min = expected - margin;
121     int expected_max = expected + margin;
122     EXPECT_LE(expected_min, actual);
123     EXPECT_LE(actual, expected_max);
124     EXPECT_GE(actual, 0);
125   }
126 
SizeBetween(int first,int last)127   int SizeBetween(int first, int last) {
128     int size = 0;
129     for (int pos = first; pos < last; ++pos)
130       size += kCacheEntries[pos].size;
131     return size;
132   }
133 
SizeAll()134   int SizeAll() { return SizeBetween(0, base::size(kCacheEntries)); }
135 
CountBetween(NetworkContext * network_context,base::Time start_time,base::Time end_time)136   static std::pair<bool, int64_t> CountBetween(NetworkContext* network_context,
137                                                base::Time start_time,
138                                                base::Time end_time) {
139     base::RunLoop run_loop;
140     std::pair<bool, int64_t> result(false, net::ERR_FAILED);
141     std::unique_ptr<HttpCacheDataCounter> own_counter =
142         HttpCacheDataCounter::CreateAndStart(
143             network_context->url_request_context(), start_time, end_time,
144             base::BindLambdaForTesting([&](HttpCacheDataCounter* counter,
145                                            bool upper_bound,
146                                            int64_t size_or_error) {
147               EXPECT_EQ(counter, own_counter.get());
148               result = std::make_pair(upper_bound, size_or_error);
149               run_loop.Quit();
150             }));
151     run_loop.Run();
152     return result;
153   }
154 
TestCountBetween(int start_index,int end_index)155   void TestCountBetween(int start_index, int end_index) {
156     DCHECK_LE(0, start_index);
157     DCHECK_LT(start_index, static_cast<int>(base::size(kCacheEntries)));
158     DCHECK_LE(0, end_index);
159     DCHECK_LT(end_index, static_cast<int>(base::size(kCacheEntries)));
160 
161     base::Time start_time;
162     ASSERT_TRUE(
163         base::Time::FromString(kCacheEntries[start_index].date, &start_time));
164 
165     base::Time end_time;
166     ASSERT_TRUE(
167         base::Time::FromString(kCacheEntries[end_index].date, &end_time));
168 
169     // The upper bound is "exclusive" but appropriximately so; make it clearly
170     // exclusive.
171     end_time -= base::TimeDelta::FromDays(1);
172 
173     auto result = CountBetween(network_context_.get(), start_time, end_time);
174     ASSERT_GE(result.second, 0);
175     if (result.first) {  // upper bound
176       ExpectClose(result.second, SizeAll());
177     } else {
178       ExpectClose(result.second, SizeBetween(start_index, end_index));
179     }
180   }
181 
182  protected:
InitNetworkContext()183   void InitNetworkContext() {
184     mojom::NetworkContextParamsPtr context_params = CreateContextParams();
185     context_params->http_cache_enabled = true;
186     context_params->http_cache_path = cache_dir_.GetPath();
187 
188     network_context_ = std::make_unique<NetworkContext>(
189         network_service_.get(),
190         network_context_remote_.BindNewPipeAndPassReceiver(),
191         std::move(context_params));
192   }
193 
194   base::test::TaskEnvironment task_environment_;
195   base::ScopedTempDir cache_dir_;
196   std::unique_ptr<NetworkService> network_service_;
197   std::unique_ptr<NetworkContext> network_context_;
198 
199   // Stores the mojo::Remote<mojom::NetworkContext> of the most recently created
200   // NetworkContext.
201   mojo::Remote<mojom::NetworkContext> network_context_remote_;
202   disk_cache::Backend* backend_ = nullptr;
203 };
204 
TEST_F(HttpCacheDataCounterTest,Basic)205 TEST_F(HttpCacheDataCounterTest, Basic) {
206   auto result =
207       CountBetween(network_context_.get(), base::Time(), base::Time::Max());
208   EXPECT_EQ(false, result.first);
209   ExpectClose(result.second, SizeAll());
210 
211   // Backwards interval to make sure we don't hit DCHECKs.
212   TestCountBetween(1, 0);
213 
214   // Technically backwards, too.
215   TestCountBetween(0, 0);
216 
217   // Empty interval.
218   TestCountBetween(0, 1);
219 
220   // Some non-empty ones.
221   TestCountBetween(0, 3);
222   TestCountBetween(2, 5);
223 }
224 
225 // Return the sensible thing (0 bytes used) when there is no cache.
TEST(HttpCacheDataCounterTestNoCache,BeSensible)226 TEST(HttpCacheDataCounterTestNoCache, BeSensible) {
227   base::test::TaskEnvironment task_environment(
228       base::test::TaskEnvironment::MainThreadType::IO);
229   std::unique_ptr<NetworkService> network_service(
230       NetworkService::CreateForTesting());
231   std::unique_ptr<NetworkContext> network_context;
232   mojo::Remote<mojom::NetworkContext> network_context_remote;
233 
234   mojom::NetworkContextParamsPtr context_params = CreateContextParams();
235   context_params->http_cache_enabled = false;
236 
237   network_context = std::make_unique<NetworkContext>(
238       network_service.get(),
239       network_context_remote.BindNewPipeAndPassReceiver(),
240       std::move(context_params));
241   auto result = HttpCacheDataCounterTest::CountBetween(
242       network_context.get(), base::Time(), base::Time::Max());
243   EXPECT_EQ(false, result.first);
244   EXPECT_EQ(0, result.second);
245 }
246 
247 }  // namespace
248 
249 }  // namespace network
250