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