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

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: changed unsigned int to size_t, key to cache_key, and added comments. Created 6 years, 5 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 requires constant use of the
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* file_name;
29 const char* cache_key;
30 };
31
32 const TestCertMetaData kCert1{"root_ca_cert.pem",
wtc 2014/06/26 23:27:53 This is missing the '=' sign: const TestCertMetaD
33 "cert:4C005EF1CF45F80D4A5A2BCFB00D4F198121E8D4"};
34
35 const TestCertMetaData kCert2{"ok_cert.pem",
36 "cert:9174C7CB9E4919604E7B1BFC430E4929DA45F65F"};
37
38 // MockTransactions are required to use the MockDiskCache backend.
39 // |key| is a cache key, and is equivalent to the key that will be
40 // used to store or retrieve certificates in the cache. |test_mode|
41 // is an integer that is used to indicate properties of the test
42 // transaction, mostly whether or not it is synchronous.
43 // For testing the DiskBasedCertCache, other data members of the struct
44 // are irrelevant. Only one MockTransaction per certificate can be used
45 // at a time.
46 MockTransaction CreateMockTransaction(const char* key, int test_mode) {
47 return {key, "", base::Time(), "", LOAD_NORMAL, "", "",
wtc 2014/06/26 23:27:53 You may need to declare a local MockTransaction va
48 base::Time(), "", test_mode, NULL, 0, OK};
49 }
50
51 // Helper class, for use with DiskBasedCertCache::Get, that will ensure that
52 // the returned certificate handle is kept alive after the callback has been
53 // executed and allow a user to WaitForResult of DiskBasedCertCache::Get.
54 class TestGetCallback {
55 public:
56 TestGetCallback() : cert_handle_(NULL) {}
57 ~TestGetCallback() { X509Certificate::FreeOSCertHandle(cert_handle_); }
58
59 // Blocks until the underlying Get() operation has succeeded.
60 void WaitForResult() { cb_.WaitForResult(); }
61
62 // Returns a Callback suitable for use with DiskBasedCertCache::Get(). The
63 // returned callback is only valid while the TestGetCallback object is still
64 // valid.
65 DiskBasedCertCache::GetCallback callback() {
66 return base::Bind(&TestGetCallback::OnGetComplete, base::Unretained(this));
67 }
68
69 // Returns the associated certificate handle.
70 const X509Certificate::OSCertHandle& cert_handle() const {
71 return cert_handle_;
72 }
73
74 private:
75 void OnGetComplete(const X509Certificate::OSCertHandle handle) {
76 if (handle)
77 cert_handle_ = X509Certificate::DupOSCertHandle(handle);
78 cb_.callback().Run(OK);
79 }
80
81 TestCompletionCallback cb_;
82 X509Certificate::OSCertHandle cert_handle_;
83 };
84
85 // Helper class, for use with DiskBasedCertCache::Set, that will store the
86 // returned key and allow a user to WaitForResult of DiskBasedCertCache::Set.
87 class TestSetCallback {
88 public:
89 TestSetCallback() {}
90 ~TestSetCallback() {}
91
92 // Blocks until the underlying Set() operation has succeeded.
93 void WaitForResult() { cb_.WaitForResult(); }
94
95 // Returns a Callback suitable for use with DiskBasedCertCache::Set(). The
96 // returned callback is only valid while the TestSetCallback object is still
97 // valid.
98 DiskBasedCertCache::SetCallback callback() {
99 return base::Bind(&TestSetCallback::OnSetComplete, base::Unretained(this));
100 }
101
102 // Returns the associated certificate handle.
103 const std::string& key() const { return key_; }
104
105 private:
106 void OnSetComplete(const std::string& key) {
107 key_ = key;
108 cb_.callback().Run(OK);
109 }
110
111 TestCompletionCallback cb_;
112 std::string key_;
113 };
114
115 // Stores the certificate corresponding to |cert_data| in |backend|. If
116 // |corrupt_data| is true, the certificate will be imported with errors
117 // so as to mimic a corrupted file on disk.
118 void ImportCert(disk_cache::Backend* backend,
119 const TestCertMetaData& cert_data,
120 bool corrupt_data) {
121 disk_cache::Entry* entry;
122 TestCompletionCallback callback;
123 int rv =
124 backend->CreateEntry(cert_data.cache_key, &entry, callback.callback());
125 EXPECT_EQ(OK, callback.GetResult(rv));
126 scoped_refptr<X509Certificate> cert(
127 ImportCertFromFile(GetTestCertsDirectory(), cert_data.file_name));
128 std::string write_data;
129 bool encoded =
130 X509Certificate::GetDEREncoded(cert->os_cert_handle(), &write_data);
131 ASSERT_TRUE(encoded);
132 if (corrupt_data) {
133 for (size_t i = 0; i < write_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(static_cast<int>(write_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.cache_key, &entry, callback.callback());
155 EXPECT_EQ(OK, callback.GetResult(rv));
156 scoped_refptr<X509Certificate> cert(
157 ImportCertFromFile(GetTestCertsDirectory(), cert_data.file_name));
158 std::string write_data;
159 bool encoded =
160 X509Certificate::GetDEREncoded(cert->os_cert_handle(), &write_data);
161 ASSERT_TRUE(encoded);
162 int entry_size = entry->GetDataSize(0 /* index */);
163 scoped_refptr<IOBuffer> buffer(new IOBuffer(entry_size));
164 rv = entry->ReadData(
165 0 /* index */, 0 /* offset */, buffer, entry_size, callback.callback());
166 EXPECT_EQ(entry_size, callback.GetResult(rv));
167 X509Certificate::OSCertHandle cached_cert_handle =
168 X509Certificate::CreateOSCertHandleFromBytes(buffer->data(), entry_size);
169 EXPECT_TRUE(X509Certificate::IsSameOSCert(cached_cert_handle,
170 cert->os_cert_handle()));
171 }
172
173 } // namespace
174
175 // ----------------------------------------------------------------------------
176
177 // Tests that a certificate can be stored in the cache.
178 TEST(DiskBasedCertCache, SetCert) {
179 ScopedMockTransaction trans1(
180 CreateMockTransaction(kCert1.cache_key, TEST_MODE_NORMAL));
181 MockDiskCache backend;
182 DiskBasedCertCache cache(&backend);
183 scoped_refptr<X509Certificate> cert(
184 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
185 ASSERT_TRUE(cert.get());
186 TestSetCallback set_callback;
187
188 cache.Set(cert->os_cert_handle(), set_callback.callback());
189 set_callback.WaitForResult();
190 EXPECT_EQ(kCert1.cache_key, set_callback.key());
191 ASSERT_NO_FATAL_FAILURE(CheckCertCached(&backend, kCert1));
192 }
193
194 // Tests that a certificate can be retrieved from the cache.
195 TEST(DiskBasedCertCache, GetCert) {
196 ScopedMockTransaction trans1(
197 CreateMockTransaction(kCert1.cache_key, TEST_MODE_NORMAL));
198 MockDiskCache backend;
199 ASSERT_NO_FATAL_FAILURE(
200 ImportCert(&backend, kCert1, false /* not corrupted */));
201 DiskBasedCertCache cache(&backend);
202 TestGetCallback get_callback;
203
204 cache.Get(kCert1.cache_key, get_callback.callback());
205 get_callback.WaitForResult();
206
207 scoped_refptr<X509Certificate> cert(
208 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
209 EXPECT_TRUE(X509Certificate::IsSameOSCert(get_callback.cert_handle(),
210 cert->os_cert_handle()));
211 }
212
213 // Tests that the DiskBasedCertCache successfully writes to the cache
214 // if the cache acts synchronously
215 TEST(DiskBasedCertCache, SyncSet) {
216 ScopedMockTransaction trans1(
217 CreateMockTransaction(kCert1.cache_key, TEST_MODE_SYNC_ALL));
218 MockDiskCache backend;
219 DiskBasedCertCache cache(&backend);
220 scoped_refptr<X509Certificate> cert(
221 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
222 ASSERT_TRUE(cert.get());
223
224 TestSetCallback set_callback;
225 cache.Set(cert->os_cert_handle(), set_callback.callback());
226 EXPECT_EQ(kCert1.cache_key, set_callback.key());
227 ASSERT_NO_FATAL_FAILURE(CheckCertCached(&backend, kCert1));
228 }
229
230 // Tests that the DiskBasedCertCache successfully reads from the cache
231 // if the cache acts synchronously
232 TEST(DiskBasedCertCache, SyncGet) {
233 ScopedMockTransaction trans1(
234 CreateMockTransaction(kCert1.cache_key, TEST_MODE_SYNC_ALL));
235 MockDiskCache backend;
236 ASSERT_NO_FATAL_FAILURE(
237 (ImportCert(&backend, kCert1, false /* not corrupted */)));
238 DiskBasedCertCache cache(&backend);
239 scoped_refptr<X509Certificate> cert(
240 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
241 ASSERT_TRUE(cert.get());
242
243 TestGetCallback get_callback;
244 cache.Get(kCert1.cache_key, get_callback.callback());
245 EXPECT_EQ(cert->os_cert_handle(), get_callback.cert_handle());
wtc 2014/06/26 23:27:53 I think you should test EXPECT_TRUE(X509Certifica
246 }
247
248 // Tests that Get will fail on a corrupted certificate.
249 TEST(DiskBasedCertCache, GetBrokenCert) {
250 ScopedMockTransaction trans1(
251 CreateMockTransaction(kCert1.cache_key, TEST_MODE_NORMAL));
252 MockDiskCache backend;
253 ASSERT_NO_FATAL_FAILURE(ImportCert(&backend, kCert1, true /* corrupted */));
254 DiskBasedCertCache cache(&backend);
255 TestGetCallback get_callback;
256
257 cache.Get(kCert1.cache_key, get_callback.callback());
258 get_callback.WaitForResult();
259
260 scoped_refptr<X509Certificate> cert(
261 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
262 EXPECT_FALSE(get_callback.cert_handle());
263 }
264
265 // Tests that attempting to retrieve a cert that is not in the cache will
266 // return NULL.
267 TEST(DiskBasedCertCache, GetUncachedCert) {
268 ScopedMockTransaction trans1(
269 CreateMockTransaction(kCert1.cache_key, TEST_MODE_NORMAL));
270 MockDiskCache backend;
271 DiskBasedCertCache cache(&backend);
272 TestGetCallback get_callback;
273
274 cache.Get(kCert1.cache_key, get_callback.callback());
275 get_callback.WaitForResult();
276 EXPECT_EQ(NULL, get_callback.cert_handle());
277 }
278
279 // Issues two requests to store a certificate in the cache
280 // (simultaneously), and checks that the DiskBasedCertCache stores the
281 // certificate to the cache (in one write rather than two).
282 TEST(DiskBasedCertCache, SetMultiple) {
283 ScopedMockTransaction trans1(
284 CreateMockTransaction(kCert1.cache_key, TEST_MODE_NORMAL));
285 MockDiskCache backend;
286 DiskBasedCertCache cache(&backend);
287 scoped_refptr<X509Certificate> cert(
288 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
289 ASSERT_TRUE(cert.get());
290 TestSetCallback set_callback1, set_callback2;
291
292 // Behind the scenes, these two operations will be combined
293 // into one operation. IgnoreCallbacks guarantees that the
294 // first Set operation is not yet complete when the second Set is
295 // called, and then IgnoreCallbacks(false) continues the
296 // (combined) operation in the |cache|.
297 MockDiskEntry::IgnoreCallbacks(true);
298 cache.Set(cert->os_cert_handle(), set_callback1.callback());
299 cache.Set(cert->os_cert_handle(), set_callback2.callback());
300 MockDiskEntry::IgnoreCallbacks(false);
301
302 set_callback1.WaitForResult();
303 set_callback2.WaitForResult();
304 EXPECT_EQ(set_callback1.key(), set_callback2.key());
305 ASSERT_NO_FATAL_FAILURE(CheckCertCached(&backend, kCert1));
306 }
307
308 // Issues two requests to store a certificate in the cache
309 // because the first transaction finishes before the second
310 // one is issued, the first cache write is overwritten.
311 TEST(DiskBasedCertCache, SetOverwrite) {
312 ScopedMockTransaction trans1(
313 CreateMockTransaction(kCert1.cache_key, TEST_MODE_NORMAL));
314 MockDiskCache backend;
315 backend.set_double_create_check(false);
316 DiskBasedCertCache cache(&backend);
317 scoped_refptr<X509Certificate> cert(
318 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
319 ASSERT_TRUE(cert.get());
320 TestSetCallback set_callback1, set_callback2;
321
322 cache.Set(cert->os_cert_handle(), set_callback1.callback());
323 set_callback1.WaitForResult();
324 cache.Set(cert->os_cert_handle(), set_callback2.callback());
325 set_callback2.WaitForResult();
326
327 EXPECT_EQ(set_callback1.key(), set_callback2.key());
328 ASSERT_NO_FATAL_FAILURE(CheckCertCached(&backend, kCert1));
329 }
330
331 // Stores a certificate in the DiskBasedCertCache, then retrieves it
332 // and makes sure it was retrieved successfully.
333 TEST(DiskBasedCertCache, SimpleSetAndGet) {
334 ScopedMockTransaction trans1(
335 CreateMockTransaction(kCert1.cache_key, TEST_MODE_NORMAL));
336 MockDiskCache backend;
337 DiskBasedCertCache cache(&backend);
338 scoped_refptr<X509Certificate> cert(
339 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
340 ASSERT_TRUE(cert.get());
341 TestSetCallback set_callback;
342 TestGetCallback get_callback;
343
344 cache.Set(cert->os_cert_handle(), set_callback.callback());
345 set_callback.WaitForResult();
346 cache.Get(set_callback.key(), get_callback.callback());
347 get_callback.WaitForResult();
348 EXPECT_TRUE(X509Certificate::IsSameOSCert(get_callback.cert_handle(),
349 cert->os_cert_handle()));
350 }
351
352 // Tests some basic functionality of the DiskBasedCertCache, with multiple
353 // set and get operations.
354 TEST(DiskBasedCertCache, BasicUsage) {
355 ScopedMockTransaction trans1(
356 CreateMockTransaction(kCert1.cache_key, TEST_MODE_SYNC_CACHE_START));
357 ScopedMockTransaction trans2(
358 CreateMockTransaction(kCert2.cache_key, TEST_MODE_NORMAL));
359 MockDiskCache backend;
360 DiskBasedCertCache cache(&backend);
361 scoped_refptr<X509Certificate> cert1(
362 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
363 scoped_refptr<X509Certificate> cert2(
364 ImportCertFromFile(GetTestCertsDirectory(), kCert2.file_name));
365 ASSERT_TRUE(cert1.get());
366 ASSERT_TRUE(cert2.get());
367 ASSERT_FALSE(X509Certificate::IsSameOSCert(cert1->os_cert_handle(),
368 cert2->os_cert_handle()));
369 TestSetCallback set_callback1, set_callback2;
370
371 // Callbacks are temporarily ignored here to guarantee the asynchronous
372 // operations of the DiskBasedCertCache are always executed in the same
373 // order.
374 MockDiskEntry::IgnoreCallbacks(true);
375 cache.Set(cert1->os_cert_handle(), set_callback1.callback());
376 cache.Set(cert2->os_cert_handle(), set_callback2.callback());
377 MockDiskEntry::IgnoreCallbacks(false);
378 set_callback1.WaitForResult();
379 set_callback2.WaitForResult();
380
381 TestGetCallback get_callback1, get_callback2;
382
383 MockDiskEntry::IgnoreCallbacks(true);
384 cache.Get(set_callback1.key(), get_callback1.callback());
385 cache.Get(set_callback2.key(), get_callback2.callback());
386 MockDiskEntry::IgnoreCallbacks(false);
387 get_callback1.WaitForResult();
388 get_callback2.WaitForResult();
389
390 EXPECT_TRUE(X509Certificate::IsSameOSCert(cert1->os_cert_handle(),
391 get_callback1.cert_handle()));
392 EXPECT_TRUE(X509Certificate::IsSameOSCert(cert2->os_cert_handle(),
393 get_callback2.cert_handle()));
394 }
395
396 // Test the result of simultaneous requests to store and retrieve a
397 // certificate from the cache, with the get operation attempting to
398 // open the cache first and therefore failing to open the entry.
399 TEST(DiskBasedCertCache, SimultaneousGetSet) {
400 ScopedMockTransaction trans1(
401 CreateMockTransaction(kCert1.cache_key, TEST_MODE_SYNC_CACHE_START));
402 MockDiskCache backend;
403 DiskBasedCertCache cache(&backend);
404 scoped_refptr<X509Certificate> cert(
405 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
406 ASSERT_TRUE(cert.get());
407
408 TestGetCallback get_callback;
409 TestSetCallback set_callback;
410
411 MockDiskEntry::IgnoreCallbacks(true);
412 cache.Get(kCert1.cache_key, get_callback.callback());
413 cache.Set(cert->os_cert_handle(), set_callback.callback());
414 MockDiskEntry::IgnoreCallbacks(false);
415 get_callback.WaitForResult();
416 set_callback.WaitForResult();
417
418 EXPECT_EQ(NULL, get_callback.cert_handle());
419 EXPECT_EQ(kCert1.cache_key, set_callback.key());
420 }
421
422 // Test the result of simultaneous requests to store and retrieve a
423 // certificate from the cache, with the get operation opening the cache
424 // after the set operation, leading to a successful read.
425 TEST(DiskBasedCertCache, SimultaneousSetGet) {
426 ScopedMockTransaction trans1(
427 CreateMockTransaction(kCert1.cache_key, TEST_MODE_SYNC_CACHE_START));
428 MockDiskCache backend;
429 DiskBasedCertCache cache(&backend);
430 scoped_refptr<X509Certificate> cert(
431 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
432 ASSERT_TRUE(cert.get());
433
434 TestSetCallback set_callback;
435 TestGetCallback get_callback;
436
437 MockDiskEntry::IgnoreCallbacks(true);
438 cache.Set(cert->os_cert_handle(), set_callback.callback());
439 cache.Get(kCert1.cache_key, get_callback.callback());
440 MockDiskEntry::IgnoreCallbacks(false);
441 set_callback.WaitForResult();
442 get_callback.WaitForResult();
443
444 EXPECT_EQ(kCert1.cache_key, set_callback.key());
445 EXPECT_TRUE(X509Certificate::IsSameOSCert(cert->os_cert_handle(),
446 get_callback.cert_handle()));
447 }
448
449 // Tests that the DiskBasedCertCache can be deleted without issues when
450 // there are pending operations in the disk cache.
451 TEST(DiskBasedCertCache, DeletedCertCache) {
452 ScopedMockTransaction trans1(
453 CreateMockTransaction(kCert1.cache_key, TEST_MODE_NORMAL));
454 MockDiskCache backend;
455 scoped_ptr<DiskBasedCertCache> cache(new DiskBasedCertCache(&backend));
456 scoped_refptr<X509Certificate> cert(
457 ImportCertFromFile(GetTestCertsDirectory(), kCert1.file_name));
458 ASSERT_TRUE(cert.get());
459 TestSetCallback set_callback;
460
461 cache->Set(cert->os_cert_handle(), set_callback.callback());
462 cache.reset();
463 set_callback.WaitForResult();
464 EXPECT_EQ(std::string(), set_callback.key());
465 }
466
467 } // 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