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));