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