Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: net/http/disk_based_cert_cache_unittest.cc

Issue 329733002: Disk Based Certificate Cache Implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Updated and improved unittests. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/http/disk_based_cert_cache.cc ('k') | net/net.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014 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 "net/http/disk_based_cert_cache.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "net/base/completion_callback.h"
10 #include "net/base/io_buffer.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/test_completion_callback.h"
13 #include "net/base/test_data_directory.h"
14 #include "net/disk_cache/memory/mem_backend_impl.h"
15 #include "net/http/mock_http_cache.h"
16 #include "net/test/cert_test_util.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace net {
20
21 namespace {
22
23 // Testing the DiskBasedCertCache requireds constant use of the
Ryan Sleevi 2014/06/25 19:31:57 s/requires/
24 // certificates in GetTestCertsDirectory(). The TestCertMetaData
25 // struct stores metadata relevant to the DiskBasedCertCache for
26 // each used test certificate.
27 struct TestCertMetaData {
28 const char* name;
Ryan Sleevi 2014/06/25 19:31:57 s/name/file_name/ - since that's what you're namin
29 const char* key;
Ryan Sleevi 2014/06/25 19:31:57 s/key/cache_key/
30 const int size;
Ryan Sleevi 2014/06/25 19:31:57 Is this supposed to be the size of the file? For s
31 };
32
33 const TestCertMetaData kCert1{
34 "root_ca_cert.pem", "cert:4C005EF1CF45F80D4A5A2BCFB00D4F198121E8D4", 759};
35
36 const TestCertMetaData kCert2{
37 "ok_cert.pem", "cert:9174C7CB9E4919604E7B1BFC430E4929DA45F65F", 888};
38
39 // MockTransactions are required to use the MockDiskCache backend.
40 // |key| is a cache key, and is equivalent to the key that will be
41 // used to store or retrieve certificates in the cache. |test_mode|
42 // is an integer that is used to indicate properties of the test
43 // transaction, mostly whether or not it is synchronous.
44 // For testing the DiskBasedCertcache, other data members of the struct
45 // are irrelevant. Only one MockTransaction per certificate can be used
46 // at a time.
47 MockTransaction GetMockTransaction(const char* key, int test_mode) {
48 return {key, "", base::Time(), "", LOAD_NORMAL, "", "",
49 base::Time(), "", test_mode, NULL, 0, OK};
50 }
51
52 // Helper class, for use with DiskBasedCertCache::Get, that will ensure that
53 // the returned certificate handle is kept alive after the callback has been
54 // executed and allow a user to WaitForResult of DiskBasedCertCache::Get.
55 class TestGetCallback {
56 public:
57 TestGetCallback() : cert_handle_(NULL) {}
58 ~TestGetCallback() { X509Certificate::FreeOSCertHandle(cert_handle_); }
59
60 // Blocks until the underlying Get() operation has succeeded.
61 void WaitForResult() { cb_.WaitForResult(); }
62
63 // Returns a Callback suitable for use with DiskBasedCertCache::Get(). The
64 // returned callback is only valid while the TestGetCallback object is still
65 // valid.
66 DiskBasedCertCache::GetCallback callback() {
67 return base::Bind(&TestGetCallback::OnGetComplete, base::Unretained(this));
68 }
69
70 // Returns the associated certificate handle.
71 const X509Certificate::OSCertHandle& cert_handle() const {
72 return cert_handle_;
73 }
74
75 private:
76 void OnGetComplete(const X509Certificate::OSCertHandle handle) {
77 if (handle)
78 cert_handle_ = X509Certificate::DupOSCertHandle(handle);
79 cb_.callback().Run(OK);
80 }
81
82 TestCompletionCallback cb_;
83 X509Certificate::OSCertHandle cert_handle_;
84 };
85
86 // Helper class, for use with DiskBasedCertCache::Set, that will store the
87 // returned key and allow a user to WaitForResult of DiskBasedCertCache::Set.
88 class TestSetCallback {
89 public:
90 TestSetCallback() {}
91 ~TestSetCallback() {}
92
93 // Blocks until the underlying Set() operation has succeeded.
94 void WaitForResult() { cb_.WaitForResult(); }
95
96 // Returns a Callback suitable for use with DiskBasedCertCache::Set(). The
97 // returned callback is only valid while the TestSetCallback object is still
98 // valid.
99 DiskBasedCertCache::SetCallback callback() {
100 return base::Bind(&TestSetCallback::OnSetComplete, base::Unretained(this));
101 }
102
103 // Returns the associated certificate handle.
104 const std::string& key() const { return key_; }
105
106 private:
107 void OnSetComplete(const std::string& key) {
108 key_ = key;
109 cb_.callback().Run(OK);
110 }
111
112 TestCompletionCallback cb_;
113 std::string key_;
114 };
115
116 // Stores the certificate corresponding to |cert_data| in |backend|. If
117 // |corrupt_data| is true, the certificate will be imported with errors
118 // so as to mimic a corrupted file on disk.
119 void ImportCert(disk_cache::Backend* backend,
120 const TestCertMetaData& cert_data,
121 bool corrupt_data) {
122 disk_cache::Entry* entry;
123 TestCompletionCallback callback;
124 int rv = backend->CreateEntry(cert_data.key, &entry, callback.callback());
125 EXPECT_EQ(OK, callback.GetResult(rv));
126 scoped_refptr<X509Certificate> cert(
127 ImportCertFromFile(GetTestCertsDirectory(), cert_data.name));
128 std::string write_data;
129 bool encoded =
130 X509Certificate::GetDEREncoded(cert->os_cert_handle(), &write_data);
131 CHECK(encoded);
Ryan Sleevi 2014/06/25 19:31:57 In unittests, you don't want to CHECK, as that can
132 if (corrupt_data) {
133 for (int i = 0; i < cert_data.size; i += 20)
134 write_data[i]++;
135 }
136 scoped_refptr<IOBuffer> buffer(new IOBuffer(write_data.size()));
137 memcpy(buffer->data(), write_data.data(), write_data.size());
138 rv = entry->WriteData(0 /* index */,
139 0 /* offset */,
140 buffer,
141 write_data.size(),
142 callback.callback(),
143 true /* truncate */);
144 ASSERT_EQ(cert_data.size, callback.GetResult(rv));
145 entry->Close();
146 }
147
148 // Checks that the the certificate corresponding to |cert_data| is an existing,
149 // correctly cached entry in |backend|.
150 void CheckCertCached(disk_cache::Backend* backend,
151 const TestCertMetaData& cert_data) {
152 disk_cache::Entry* entry;
153 TestCompletionCallback callback;
154 int rv = backend->OpenEntry(cert_data.key, &entry, callback.callback());
155 EXPECT_EQ(OK, callback.GetResult(rv));
156 scoped_refptr<X509Certificate> cert(
157 ImportCertFromFile(GetTestCertsDirectory(), cert_data.name));
158 std::string write_data;
159 bool encoded =
160 X509Certificate::GetDEREncoded(cert->os_cert_handle(), &write_data);
161 ASSERT_TRUE(encoded);
162 scoped_refptr<IOBuffer> buffer(new IOBuffer(cert_data.size));
163 rv = entry->ReadData(0 /* index */,
164 0 /* offset */,
165 buffer,
166 cert_data.size,
167 callback.callback());
168 EXPECT_EQ(cert_data.size, callback.GetResult(rv));
169 X509Certificate::OSCertHandle cached_cert_handle =
170 X509Certificate::CreateOSCertHandleFromBytes(buffer->data(),
171 cert_data.size);
172 EXPECT_TRUE(X509Certificate::IsSameOSCert(cached_cert_handle,
173 cert->os_cert_handle()));
174 }
175
176 } // namespace
177
178 // ----------------------------------------------------------------------------
179
180 // Tests that a certificate can be stored in the cache.
181 TEST(DiskBasedCertCache, SetCert) {
182 ScopedMockTransaction trans1(
183 GetMockTransaction(kCert1.key, TEST_MODE_NORMAL));
184 MockDiskCache backend;
185 DiskBasedCertCache cache(&backend);
186 scoped_refptr<X509Certificate> cert(
187 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
188 ASSERT_TRUE(cert.get());
189 TestSetCallback set_callback;
190
191 cache.Set(cert->os_cert_handle(), set_callback.callback());
192 set_callback.WaitForResult();
193 EXPECT_EQ(kCert1.key, set_callback.key());
194 CheckCertCached(&backend, kCert1);
195 }
196
197 // Tests that a certificate can be retrieved from the cache.
198 TEST(DiskBasedCertCache, GetCert) {
199 ScopedMockTransaction trans1(
200 GetMockTransaction(kCert1.key, TEST_MODE_NORMAL));
201 MockDiskCache backend;
202 ImportCert(&backend, kCert1, false /* not corrupted */);
Ryan Sleevi 2014/06/25 19:31:57 If ImportCert had a failure, this test will contin
203 DiskBasedCertCache cache(&backend);
204 TestGetCallback get_callback;
205
206 cache.Get(kCert1.key, get_callback.callback());
207 get_callback.WaitForResult();
208
209 scoped_refptr<X509Certificate> cert(
210 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
211 EXPECT_TRUE(X509Certificate::IsSameOSCert(get_callback.cert_handle(),
212 cert->os_cert_handle()));
213 }
214
215 // Tests that the DiskBasedCertCache successfully writes to the cache
216 // if the cache acts synchronously
217 TEST(DiskBasedCertCache, SyncSet) {
218 ScopedMockTransaction trans1(
219 GetMockTransaction(kCert1.key, TEST_MODE_SYNC_ALL));
220 MockDiskCache backend;
221 DiskBasedCertCache cache(&backend);
222 scoped_refptr<X509Certificate> cert(
223 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
224 ASSERT_TRUE(cert.get());
225
226 TestSetCallback set_callback;
227 cache.Set(cert->os_cert_handle(), set_callback.callback());
228 EXPECT_EQ(kCert1.key, set_callback.key());
229 CheckCertCached(&backend, kCert1);
230 }
231
232 // Tests that the DiskBasedCertCache successfully reads from the cache
233 // if the cache acts synchronously
234 TEST(DiskBasedCertCache, SyncGet) {
235 ScopedMockTransaction trans1(
236 GetMockTransaction(kCert1.key, TEST_MODE_SYNC_ALL));
237 MockDiskCache backend;
238 ImportCert(&backend, kCert1, false /* not corrupted */);
239 DiskBasedCertCache cache(&backend);
240 scoped_refptr<X509Certificate> cert(
241 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
242 ASSERT_TRUE(cert.get());
243
244 TestGetCallback get_callback;
245 cache.Get(kCert1.key, get_callback.callback());
246 EXPECT_EQ(cert->os_cert_handle(), get_callback.cert_handle());
247 }
248
249 // Tests that Get will fail on a corrupted certificate.
250 TEST(DiskBasedCertCache, GetBrokenCert) {
251 ScopedMockTransaction trans1(
252 GetMockTransaction(kCert1.key, TEST_MODE_NORMAL));
253 MockDiskCache backend;
254 ImportCert(&backend, kCert1, true /* corrupted */);
255 DiskBasedCertCache cache(&backend);
256 TestGetCallback get_callback;
257
258 cache.Get(kCert1.key, get_callback.callback());
259 get_callback.WaitForResult();
260
261 scoped_refptr<X509Certificate> cert(
262 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
263 EXPECT_FALSE(get_callback.cert_handle());
264 }
265
266 // Tests that attempting to retrieve a cert that is not in the cache will
267 // return NULL.
268 TEST(DiskBasedCertCache, GetUncachedCert) {
269 ScopedMockTransaction trans1(
270 GetMockTransaction(kCert1.key, TEST_MODE_NORMAL));
271 MockDiskCache backend;
272 DiskBasedCertCache cache(&backend);
273 TestGetCallback get_callback;
274
275 cache.Get(kCert1.key, get_callback.callback());
276 get_callback.WaitForResult();
277 EXPECT_EQ(NULL, get_callback.cert_handle());
278 }
279
280 // Issues two requests to store a certificate in the cache
281 // (simultaneously), and checks that the DiskBasedCertCache stores the
282 // certificate to the cache (in one write rather than two).
283 TEST(DiskBasedCertCache, SetMultiple) {
284 ScopedMockTransaction trans1(
285 GetMockTransaction(kCert1.key, TEST_MODE_NORMAL));
286 MockDiskCache backend;
287 DiskBasedCertCache cache(&backend);
288 scoped_refptr<X509Certificate> cert(
289 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
290 ASSERT_TRUE(cert.get());
291 TestSetCallback set_callback1, set_callback2;
292
293 cache.Set(cert->os_cert_handle(), set_callback1.callback());
294 cache.Set(cert->os_cert_handle(), set_callback2.callback());
Ryan Sleevi 2014/06/25 19:31:57 Can you add a note that describes how this is work
295 set_callback1.WaitForResult();
296 set_callback2.WaitForResult();
297 EXPECT_EQ(set_callback1.key(), set_callback2.key());
298 CheckCertCached(&backend, kCert1);
299 }
300
301 // Stores a certificate in the DiskBasedCertCache, then retrieves it
302 // and makes sure it was retrieved successfully.
303 TEST(DiskBasedCertCache, SimpleSetAndGet) {
304 ScopedMockTransaction trans1(
305 GetMockTransaction(kCert1.key, TEST_MODE_NORMAL));
306 MockDiskCache backend;
307 DiskBasedCertCache cache(&backend);
308 scoped_refptr<X509Certificate> cert(
309 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
310 ASSERT_TRUE(cert.get());
311 TestSetCallback set_callback;
312 TestGetCallback get_callback;
313
314 cache.Set(cert->os_cert_handle(), set_callback.callback());
315 set_callback.WaitForResult();
316 cache.Get(set_callback.key(), get_callback.callback());
317 get_callback.WaitForResult();
318 EXPECT_TRUE(X509Certificate::IsSameOSCert(get_callback.cert_handle(),
319 cert->os_cert_handle()));
320 }
321
322 // Tests some basic functionality of the DiskBasedCertCache, with multiple
323 // set and get operations.
324 TEST(DiskBasedCertCache, BasicUsage) {
325 ScopedMockTransaction trans1(
326 GetMockTransaction(kCert1.key, TEST_MODE_SYNC_CACHE_START));
327 ScopedMockTransaction trans2(
328 GetMockTransaction(kCert2.key, TEST_MODE_NORMAL));
329 MockDiskCache backend;
330 DiskBasedCertCache cache(&backend);
331 scoped_refptr<X509Certificate> cert1(
332 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
333 scoped_refptr<X509Certificate> cert2(
334 ImportCertFromFile(GetTestCertsDirectory(), kCert2.name));
335 ASSERT_TRUE(cert1.get());
336 ASSERT_TRUE(cert2.get());
337 ASSERT_FALSE(X509Certificate::IsSameOSCert(cert1->os_cert_handle(),
338 cert2->os_cert_handle()));
339 TestSetCallback set_callback1, set_callback2;
340
341 cache.Set(cert1->os_cert_handle(), set_callback1.callback());
342 cache.Set(cert2->os_cert_handle(), set_callback2.callback());
343 set_callback1.WaitForResult();
344 set_callback2.WaitForResult();
345
346 TestGetCallback get_callback1, get_callback2;
347
348 cache.Get(set_callback1.key(), get_callback1.callback());
349 cache.Get(set_callback2.key(), get_callback2.callback());
350
351 get_callback1.WaitForResult();
352 get_callback2.WaitForResult();
353
354 EXPECT_TRUE(X509Certificate::IsSameOSCert(cert1->os_cert_handle(),
355 get_callback1.cert_handle()));
356 EXPECT_TRUE(X509Certificate::IsSameOSCert(cert2->os_cert_handle(),
357 get_callback2.cert_handle()));
358 }
359
360 // Test the result of simultaneous requests to store and retrieve a
361 // certificate from the cache, with the get operation attempting to
362 // open the cache first and therefore failing to open the entry.
363 TEST(DiskBasedCertCache, SimultaneousGetSet) {
364 ScopedMockTransaction trans1(
365 GetMockTransaction(kCert1.key, TEST_MODE_SYNC_CACHE_START));
366 MockDiskCache backend;
367 DiskBasedCertCache cache(&backend);
368 scoped_refptr<X509Certificate> cert(
369 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
370 ASSERT_TRUE(cert.get());
371
372 TestGetCallback get_callback;
373 TestSetCallback set_callback;
374
375 MockDiskEntry::IgnoreCallbacks(true);
376 cache.Get(kCert1.key, get_callback.callback());
377 cache.Set(cert->os_cert_handle(), set_callback.callback());
378 MockDiskEntry::IgnoreCallbacks(false);
379 get_callback.WaitForResult();
380 set_callback.WaitForResult();
381
382 EXPECT_EQ(NULL, get_callback.cert_handle());
383 EXPECT_EQ(kCert1.key, set_callback.key());
384 }
385
386 // Test the result of simultaneous requests to store and retrieve a
387 // certificate from the cache, with the get operation opening the cache
388 // after the set operation, leading to a successful read.
389 TEST(DiskBasedCertCache, SimultaneousSetGet) {
390 ScopedMockTransaction trans1(
391 GetMockTransaction(kCert1.key, TEST_MODE_SYNC_CACHE_START));
392 MockDiskCache backend;
393 DiskBasedCertCache cache(&backend);
394 scoped_refptr<X509Certificate> cert(
395 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
396 ASSERT_TRUE(cert.get());
397
398 TestSetCallback set_callback;
399 TestGetCallback get_callback;
400
401 MockDiskEntry::IgnoreCallbacks(true);
402 cache.Set(cert->os_cert_handle(), set_callback.callback());
403 cache.Get(kCert1.key, get_callback.callback());
404 MockDiskEntry::IgnoreCallbacks(false);
405 set_callback.WaitForResult();
406 get_callback.WaitForResult();
407
408 EXPECT_EQ(kCert1.key, set_callback.key());
409 EXPECT_TRUE(X509Certificate::IsSameOSCert(cert->os_cert_handle(),
410 get_callback.cert_handle()));
411 }
412
413 // Tests that the DiskBasedCertCache can be deleted without issues when
414 // there are pending operations in the disk cache.
415 TEST(DiskBasedCertCache, DeletedCertCache) {
416 ScopedMockTransaction trans1(
417 GetMockTransaction(kCert1.key, TEST_MODE_NORMAL));
418 MockDiskCache backend;
419 scoped_ptr<DiskBasedCertCache> cache(new DiskBasedCertCache(&backend));
420 scoped_refptr<X509Certificate> cert(
421 ImportCertFromFile(GetTestCertsDirectory(), kCert1.name));
422 ASSERT_TRUE(cert.get());
423 TestSetCallback set_callback;
424
425 cache->Set(cert->os_cert_handle(), set_callback.callback());
426 cache.reset();
427 set_callback.WaitForResult();
428 EXPECT_EQ(std::string(), set_callback.key());
429 }
430
431 } // namespace net
OLDNEW
« no previous file with comments | « net/http/disk_based_cert_cache.cc ('k') | net/net.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698