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