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.
5 #include "net/http/disk_based_cert_cache.h"
10 #include "base/callback_helpers.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/net_errors.h"
17 #include "net/disk_cache/disk_cache.h"
23 // TODO(brandonsalmon): change this number to improve performance.
24 const size_t kMemoryCacheMaxSize
= 30;
26 // Used to obtain a unique cache key for a certificate in the form of
28 std::string
GetCacheKeyForCert(
29 const X509Certificate::OSCertHandle cert_handle
) {
30 SHA1HashValue fingerprint
=
31 X509Certificate::CalculateFingerprint(cert_handle
);
34 base::HexEncode(fingerprint
.data
, arraysize(fingerprint
.data
));
40 DISK_CACHE_ENTRY_CORRUPT
,
45 void RecordCacheResult(CacheResult result
) {
46 UMA_HISTOGRAM_ENUMERATION(
47 "DiskBasedCertCache.CertIoCacheResult", result
, CACHE_RESULT_MAX
);
52 // WriteWorkers represent pending SetCertificate jobs in the DiskBasedCertCache.
53 // Each certificate requested to be stored is assigned a WriteWorker.
54 // The same certificate should not have multiple WriteWorkers at the same
55 // time; instead, add a user callback to the existing WriteWorker.
56 class DiskBasedCertCache::WriteWorker
{
58 // |backend| is the backend to store |certificate| in, using
59 // |key| as the key for the disk_cache::Entry.
60 // |cleanup_callback| is called to clean up this ReadWorker,
61 // regardless of success or failure.
62 WriteWorker(disk_cache::Backend
* backend
,
63 const std::string
& key
,
64 X509Certificate::OSCertHandle cert_handle
,
65 const base::Closure
& cleanup_callback
);
69 // Writes the given certificate to the cache. On completion, will invoke all
73 // Adds a callback to the set of callbacks to be run when this
74 // WriteWorker finishes processing.
75 void AddCallback(const SetCallback
& user_callback
);
77 // Signals the WriteWorker to abort early. The WriteWorker will be destroyed
78 // upon the completion of any pending callbacks. User callbacks will be
79 // invoked with an empty string.
87 STATE_CREATE_COMPLETE
,
93 void OnIOComplete(int rv
);
97 int DoOpenComplete(int rv
);
99 int DoCreateComplete(int rv
);
101 int DoWriteComplete(int rv
);
105 // Invokes all of the |user_callbacks_|
106 void RunCallbacks(int rv
);
108 disk_cache::Backend
* backend_
;
109 const X509Certificate::OSCertHandle cert_handle_
;
113 disk_cache::Entry
* entry_
;
115 scoped_refptr
<IOBuffer
> buffer_
;
118 base::Closure cleanup_callback_
;
119 std::vector
<SetCallback
> user_callbacks_
;
120 CompletionCallback io_callback_
;
123 DiskBasedCertCache::WriteWorker::WriteWorker(
124 disk_cache::Backend
* backend
,
125 const std::string
& key
,
126 X509Certificate::OSCertHandle cert_handle
,
127 const base::Closure
& cleanup_callback
)
129 cert_handle_(X509Certificate::DupOSCertHandle(cert_handle
)),
133 next_state_(STATE_NONE
),
135 cleanup_callback_(cleanup_callback
),
137 base::Bind(&WriteWorker::OnIOComplete
, base::Unretained(this))) {
140 DiskBasedCertCache::WriteWorker::~WriteWorker() {
142 X509Certificate::FreeOSCertHandle(cert_handle_
);
147 void DiskBasedCertCache::WriteWorker::Start() {
148 DCHECK_EQ(STATE_NONE
, next_state_
);
150 next_state_
= STATE_OPEN
;
153 if (rv
== ERR_IO_PENDING
)
159 void DiskBasedCertCache::WriteWorker::AddCallback(
160 const SetCallback
& user_callback
) {
161 user_callbacks_
.push_back(user_callback
);
164 void DiskBasedCertCache::WriteWorker::Cancel() {
168 void DiskBasedCertCache::WriteWorker::OnIOComplete(int rv
) {
176 if (rv
== ERR_IO_PENDING
)
182 int DiskBasedCertCache::WriteWorker::DoLoop(int rv
) {
184 State state
= next_state_
;
185 next_state_
= STATE_NONE
;
190 case STATE_OPEN_COMPLETE
:
191 rv
= DoOpenComplete(rv
);
196 case STATE_CREATE_COMPLETE
:
197 rv
= DoCreateComplete(rv
);
202 case STATE_WRITE_COMPLETE
:
203 rv
= DoWriteComplete(rv
);
209 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
214 int DiskBasedCertCache::WriteWorker::DoOpen() {
215 next_state_
= STATE_OPEN_COMPLETE
;
216 return backend_
->OpenEntry(key_
, &entry_
, io_callback_
);
219 int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv
) {
220 // The entry doesn't exist yet, so we should create it.
222 next_state_
= STATE_CREATE
;
226 next_state_
= STATE_WRITE
;
230 int DiskBasedCertCache::WriteWorker::DoCreate() {
231 next_state_
= STATE_CREATE_COMPLETE
;
232 return backend_
->CreateEntry(key_
, &entry_
, io_callback_
);
235 int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv
) {
239 next_state_
= STATE_WRITE
;
243 int DiskBasedCertCache::WriteWorker::DoWrite() {
244 std::string write_data
;
245 bool encoded
= X509Certificate::GetDEREncoded(cert_handle_
, &write_data
);
250 buffer_
= new IOBuffer(write_data
.size());
251 io_buf_len_
= write_data
.size();
252 memcpy(buffer_
->data(), write_data
.data(), io_buf_len_
);
254 next_state_
= STATE_WRITE_COMPLETE
;
256 return entry_
->WriteData(0 /* index */,
261 true /* truncate */);
264 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv
) {
265 if (rv
< io_buf_len_
)
271 void DiskBasedCertCache::WriteWorker::Finish(int rv
) {
272 cleanup_callback_
.Run();
273 cleanup_callback_
.Reset();
278 void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv
) {
283 for (std::vector
<SetCallback
>::const_iterator it
= user_callbacks_
.begin();
284 it
!= user_callbacks_
.end();
288 user_callbacks_
.clear();
291 // ReadWorkers represent pending GetCertificate jobs in the DiskBasedCertCache.
292 // Each certificate requested to be retrieved from the cache is assigned a
293 // ReadWorker. The same |key| should not have multiple ReadWorkers at the
294 // same time; instead, call AddCallback to add a user callback to the
295 // existing ReadWorker.
296 class DiskBasedCertCache::ReadWorker
{
298 // |backend| is the backend to read |certificate| from, using
299 // |key| as the key for the disk_cache::Entry.
300 // |cleanup_callback| is called to clean up this ReadWorker,
301 // regardless of success or failure.
302 ReadWorker(disk_cache::Backend
* backend
,
303 const std::string
& key
,
304 const GetCallback
& cleanup_callback
);
308 // Reads the given certificate from the cache. On completion, will invoke all
312 // Adds a callback to the set of callbacks to be run when this
313 // ReadWorker finishes processing.
314 void AddCallback(const GetCallback
& user_callback
);
316 // Signals the ReadWorker to abort early. The ReadWorker will be destroyed
317 // upon the completion of any pending callbacks. User callbacks will be
318 // invoked with a NULL cert handle.
330 void OnIOComplete(int rv
);
333 int DoOpenComplete(int rv
);
335 int DoReadComplete(int rv
);
338 // Invokes all of |user_callbacks_|
341 disk_cache::Backend
* backend_
;
342 X509Certificate::OSCertHandle cert_handle_
;
346 disk_cache::Entry
* entry_
;
349 scoped_refptr
<IOBuffer
> buffer_
;
352 GetCallback cleanup_callback_
;
353 std::vector
<GetCallback
> user_callbacks_
;
354 CompletionCallback io_callback_
;
357 DiskBasedCertCache::ReadWorker::ReadWorker(disk_cache::Backend
* backend
,
358 const std::string
& key
,
359 const GetCallback
& cleanup_callback
)
365 next_state_(STATE_NONE
),
367 cleanup_callback_(cleanup_callback
),
369 base::Bind(&ReadWorker::OnIOComplete
, base::Unretained(this))) {
372 DiskBasedCertCache::ReadWorker::~ReadWorker() {
376 X509Certificate::FreeOSCertHandle(cert_handle_
);
379 void DiskBasedCertCache::ReadWorker::Start() {
380 DCHECK_EQ(STATE_NONE
, next_state_
);
381 next_state_
= STATE_OPEN
;
384 if (rv
== ERR_IO_PENDING
)
390 void DiskBasedCertCache::ReadWorker::AddCallback(
391 const GetCallback
& user_callback
) {
392 user_callbacks_
.push_back(user_callback
);
395 void DiskBasedCertCache::ReadWorker::Cancel() {
399 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv
) {
407 if (rv
== ERR_IO_PENDING
)
413 int DiskBasedCertCache::ReadWorker::DoLoop(int rv
) {
415 State state
= next_state_
;
416 next_state_
= STATE_NONE
;
421 case STATE_OPEN_COMPLETE
:
422 rv
= DoOpenComplete(rv
);
427 case STATE_READ_COMPLETE
:
428 rv
= DoReadComplete(rv
);
434 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
439 int DiskBasedCertCache::ReadWorker::DoOpen() {
440 next_state_
= STATE_OPEN_COMPLETE
;
441 return backend_
->OpenEntry(key_
, &entry_
, io_callback_
);
444 int DiskBasedCertCache::ReadWorker::DoOpenComplete(int rv
) {
446 RecordCacheResult(DISK_CACHE_ERROR
);
450 next_state_
= STATE_READ
;
454 int DiskBasedCertCache::ReadWorker::DoRead() {
455 next_state_
= STATE_READ_COMPLETE
;
456 io_buf_len_
= entry_
->GetDataSize(0 /* index */);
457 buffer_
= new IOBuffer(io_buf_len_
);
458 return entry_
->ReadData(
459 0 /* index */, 0 /* offset */, buffer_
.get(), io_buf_len_
, io_callback_
);
462 int DiskBasedCertCache::ReadWorker::DoReadComplete(int rv
) {
463 // The cache should return the entire buffer length. If it does not,
464 // it is probably indicative of an issue other than corruption.
465 if (rv
< io_buf_len_
) {
466 RecordCacheResult(DISK_CACHE_ERROR
);
469 cert_handle_
= X509Certificate::CreateOSCertHandleFromBytes(buffer_
->data(),
472 RecordCacheResult(DISK_CACHE_ENTRY_CORRUPT
);
476 RecordCacheResult(DISK_CACHE_HIT
);
480 void DiskBasedCertCache::ReadWorker::Finish(int rv
) {
481 cleanup_callback_
.Run(cert_handle_
);
482 cleanup_callback_
.Reset();
487 void DiskBasedCertCache::ReadWorker::RunCallbacks() {
488 for (std::vector
<GetCallback
>::const_iterator it
= user_callbacks_
.begin();
489 it
!= user_callbacks_
.end();
491 it
->Run(cert_handle_
);
493 user_callbacks_
.clear();
496 void DiskBasedCertCache::CertFree::operator()(
497 X509Certificate::OSCertHandle cert_handle
) {
498 X509Certificate::FreeOSCertHandle(cert_handle
);
501 DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend
* backend
)
503 mru_cert_cache_(kMemoryCacheMaxSize
),
505 mem_cache_misses_(0),
506 weak_factory_(this) {
510 DiskBasedCertCache::~DiskBasedCertCache() {
511 for (WriteWorkerMap::iterator it
= write_worker_map_
.begin();
512 it
!= write_worker_map_
.end();
514 it
->second
->Cancel();
516 for (ReadWorkerMap::iterator it
= read_worker_map_
.begin();
517 it
!= read_worker_map_
.end();
519 it
->second
->Cancel();
523 void DiskBasedCertCache::GetCertificate(const std::string
& key
,
524 const GetCallback
& cb
) {
525 DCHECK(!key
.empty());
527 // If the handle is already in the MRU cache, just return that (via callback).
528 // Note, this will also bring the cert_handle to the front of the recency
529 // list in the MRU cache.
530 MRUCertCache::iterator mru_it
= mru_cert_cache_
.Get(key
);
531 if (mru_it
!= mru_cert_cache_
.end()) {
532 RecordCacheResult(MEMORY_CACHE_HIT
);
534 cb
.Run(mru_it
->second
);
539 ReadWorkerMap::iterator it
= read_worker_map_
.find(key
);
541 if (it
== read_worker_map_
.end()) {
543 new ReadWorker(backend_
,
545 base::Bind(&DiskBasedCertCache::FinishedReadOperation
,
546 weak_factory_
.GetWeakPtr(),
548 read_worker_map_
[key
] = worker
;
549 worker
->AddCallback(cb
);
552 it
->second
->AddCallback(cb
);
556 void DiskBasedCertCache::SetCertificate(
557 const X509Certificate::OSCertHandle cert_handle
,
558 const SetCallback
& cb
) {
559 DCHECK(!cb
.is_null());
561 std::string key
= GetCacheKeyForCert(cert_handle
);
563 WriteWorkerMap::iterator it
= write_worker_map_
.find(key
);
565 if (it
== write_worker_map_
.end()) {
566 WriteWorker
* worker
=
567 new WriteWorker(backend_
,
570 base::Bind(&DiskBasedCertCache::FinishedWriteOperation
,
571 weak_factory_
.GetWeakPtr(),
574 write_worker_map_
[key
] = worker
;
575 worker
->AddCallback(cb
);
578 it
->second
->AddCallback(cb
);
582 void DiskBasedCertCache::FinishedReadOperation(
583 const std::string
& key
,
584 X509Certificate::OSCertHandle cert_handle
) {
586 mru_cert_cache_
.Put(key
, X509Certificate::DupOSCertHandle(cert_handle
));
587 read_worker_map_
.erase(key
);
590 void DiskBasedCertCache::FinishedWriteOperation(
591 const std::string
& key
,
592 X509Certificate::OSCertHandle cert_handle
) {
593 write_worker_map_
.erase(key
);
595 mru_cert_cache_
.Put(key
, X509Certificate::DupOSCertHandle(cert_handle
));