Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / http / disk_based_cert_cache.cc
blob913e1b7fba03ff1c1e1dfe30da90e8c4baaaa058
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"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/metrics/histogram_macros.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"
19 namespace net {
21 namespace {
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
27 // "cert:<hash>".
28 std::string GetCacheKeyForCert(
29 const X509Certificate::OSCertHandle cert_handle) {
30 SHA1HashValue fingerprint =
31 X509Certificate::CalculateFingerprint(cert_handle);
33 return "cert:" +
34 base::HexEncode(fingerprint.data, arraysize(fingerprint.data));
37 enum CacheResult {
38 MEMORY_CACHE_HIT = 0,
39 DISK_CACHE_HIT,
40 DISK_CACHE_ENTRY_CORRUPT,
41 DISK_CACHE_ERROR,
42 CACHE_RESULT_MAX
45 void RecordCacheResult(CacheResult result) {
46 UMA_HISTOGRAM_ENUMERATION(
47 "DiskBasedCertCache.CertIoCacheResult", result, CACHE_RESULT_MAX);
50 } // namespace
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 {
57 public:
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);
67 ~WriteWorker();
69 // Writes the given certificate to the cache. On completion, will invoke all
70 // user callbacks.
71 void Start();
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.
80 void Cancel();
82 private:
83 enum State {
84 STATE_OPEN,
85 STATE_OPEN_COMPLETE,
86 STATE_CREATE,
87 STATE_CREATE_COMPLETE,
88 STATE_WRITE,
89 STATE_WRITE_COMPLETE,
90 STATE_NONE
93 void OnIOComplete(int rv);
94 int DoLoop(int rv);
96 int DoOpen();
97 int DoOpenComplete(int rv);
98 int DoCreate();
99 int DoCreateComplete(int rv);
100 int DoWrite();
101 int DoWriteComplete(int rv);
103 void Finish(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_;
110 std::string key_;
111 bool canceled_;
113 disk_cache::Entry* entry_;
114 State next_state_;
115 scoped_refptr<IOBuffer> buffer_;
116 int io_buf_len_;
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)
128 : backend_(backend),
129 cert_handle_(X509Certificate::DupOSCertHandle(cert_handle)),
130 key_(key),
131 canceled_(false),
132 entry_(NULL),
133 next_state_(STATE_NONE),
134 io_buf_len_(0),
135 cleanup_callback_(cleanup_callback),
136 io_callback_(
137 base::Bind(&WriteWorker::OnIOComplete, base::Unretained(this))) {
140 DiskBasedCertCache::WriteWorker::~WriteWorker() {
141 if (cert_handle_)
142 X509Certificate::FreeOSCertHandle(cert_handle_);
143 if (entry_)
144 entry_->Close();
147 void DiskBasedCertCache::WriteWorker::Start() {
148 DCHECK_EQ(STATE_NONE, next_state_);
150 next_state_ = STATE_OPEN;
151 int rv = DoLoop(OK);
153 if (rv == ERR_IO_PENDING)
154 return;
156 Finish(rv);
159 void DiskBasedCertCache::WriteWorker::AddCallback(
160 const SetCallback& user_callback) {
161 user_callbacks_.push_back(user_callback);
164 void DiskBasedCertCache::WriteWorker::Cancel() {
165 canceled_ = true;
168 void DiskBasedCertCache::WriteWorker::OnIOComplete(int rv) {
169 if (canceled_) {
170 Finish(ERR_FAILED);
171 return;
174 rv = DoLoop(rv);
176 if (rv == ERR_IO_PENDING)
177 return;
179 Finish(rv);
182 int DiskBasedCertCache::WriteWorker::DoLoop(int rv) {
183 do {
184 State state = next_state_;
185 next_state_ = STATE_NONE;
186 switch (state) {
187 case STATE_OPEN:
188 rv = DoOpen();
189 break;
190 case STATE_OPEN_COMPLETE:
191 rv = DoOpenComplete(rv);
192 break;
193 case STATE_CREATE:
194 rv = DoCreate();
195 break;
196 case STATE_CREATE_COMPLETE:
197 rv = DoCreateComplete(rv);
198 break;
199 case STATE_WRITE:
200 rv = DoWrite();
201 break;
202 case STATE_WRITE_COMPLETE:
203 rv = DoWriteComplete(rv);
204 break;
205 case STATE_NONE:
206 NOTREACHED();
207 break;
209 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
211 return rv;
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.
221 if (rv < 0) {
222 next_state_ = STATE_CREATE;
223 return OK;
226 next_state_ = STATE_WRITE;
227 return OK;
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) {
236 if (rv < 0)
237 return rv;
239 next_state_ = STATE_WRITE;
240 return OK;
243 int DiskBasedCertCache::WriteWorker::DoWrite() {
244 std::string write_data;
245 bool encoded = X509Certificate::GetDEREncoded(cert_handle_, &write_data);
247 if (!encoded)
248 return ERR_FAILED;
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 */,
257 0 /* offset */,
258 buffer_.get(),
259 write_data.size(),
260 io_callback_,
261 true /* truncate */);
264 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv) {
265 if (rv < io_buf_len_)
266 return ERR_FAILED;
268 return OK;
271 void DiskBasedCertCache::WriteWorker::Finish(int rv) {
272 cleanup_callback_.Run();
273 cleanup_callback_.Reset();
274 RunCallbacks(rv);
275 delete this;
278 void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv) {
279 std::string key;
280 if (rv >= 0)
281 key = key_;
283 for (std::vector<SetCallback>::const_iterator it = user_callbacks_.begin();
284 it != user_callbacks_.end();
285 ++it) {
286 it->Run(key);
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 {
297 public:
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);
306 ~ReadWorker();
308 // Reads the given certificate from the cache. On completion, will invoke all
309 // user callbacks.
310 void Start();
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.
319 void Cancel();
321 private:
322 enum State {
323 STATE_OPEN,
324 STATE_OPEN_COMPLETE,
325 STATE_READ,
326 STATE_READ_COMPLETE,
327 STATE_NONE
330 void OnIOComplete(int rv);
331 int DoLoop(int rv);
332 int DoOpen();
333 int DoOpenComplete(int rv);
334 int DoRead();
335 int DoReadComplete(int rv);
336 void Finish(int rv);
338 // Invokes all of |user_callbacks_|
339 void RunCallbacks();
341 disk_cache::Backend* backend_;
342 X509Certificate::OSCertHandle cert_handle_;
343 std::string key_;
344 bool canceled_;
346 disk_cache::Entry* entry_;
348 State next_state_;
349 scoped_refptr<IOBuffer> buffer_;
350 int io_buf_len_;
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)
360 : backend_(backend),
361 cert_handle_(NULL),
362 key_(key),
363 canceled_(false),
364 entry_(NULL),
365 next_state_(STATE_NONE),
366 io_buf_len_(0),
367 cleanup_callback_(cleanup_callback),
368 io_callback_(
369 base::Bind(&ReadWorker::OnIOComplete, base::Unretained(this))) {
372 DiskBasedCertCache::ReadWorker::~ReadWorker() {
373 if (entry_)
374 entry_->Close();
375 if (cert_handle_)
376 X509Certificate::FreeOSCertHandle(cert_handle_);
379 void DiskBasedCertCache::ReadWorker::Start() {
380 DCHECK_EQ(STATE_NONE, next_state_);
381 next_state_ = STATE_OPEN;
382 int rv = DoLoop(OK);
384 if (rv == ERR_IO_PENDING)
385 return;
387 Finish(rv);
390 void DiskBasedCertCache::ReadWorker::AddCallback(
391 const GetCallback& user_callback) {
392 user_callbacks_.push_back(user_callback);
395 void DiskBasedCertCache::ReadWorker::Cancel() {
396 canceled_ = true;
399 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv) {
400 if (canceled_) {
401 Finish(ERR_FAILED);
402 return;
405 rv = DoLoop(rv);
407 if (rv == ERR_IO_PENDING)
408 return;
410 Finish(rv);
413 int DiskBasedCertCache::ReadWorker::DoLoop(int rv) {
414 do {
415 State state = next_state_;
416 next_state_ = STATE_NONE;
417 switch (state) {
418 case STATE_OPEN:
419 rv = DoOpen();
420 break;
421 case STATE_OPEN_COMPLETE:
422 rv = DoOpenComplete(rv);
423 break;
424 case STATE_READ:
425 rv = DoRead();
426 break;
427 case STATE_READ_COMPLETE:
428 rv = DoReadComplete(rv);
429 break;
430 case STATE_NONE:
431 NOTREACHED();
432 break;
434 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
436 return rv;
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) {
445 if (rv < 0) {
446 RecordCacheResult(DISK_CACHE_ERROR);
447 return rv;
450 next_state_ = STATE_READ;
451 return OK;
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);
467 return ERR_FAILED;
469 cert_handle_ = X509Certificate::CreateOSCertHandleFromBytes(buffer_->data(),
470 io_buf_len_);
471 if (!cert_handle_) {
472 RecordCacheResult(DISK_CACHE_ENTRY_CORRUPT);
473 return ERR_FAILED;
476 RecordCacheResult(DISK_CACHE_HIT);
477 return OK;
480 void DiskBasedCertCache::ReadWorker::Finish(int rv) {
481 cleanup_callback_.Run(cert_handle_);
482 cleanup_callback_.Reset();
483 RunCallbacks();
484 delete this;
487 void DiskBasedCertCache::ReadWorker::RunCallbacks() {
488 for (std::vector<GetCallback>::const_iterator it = user_callbacks_.begin();
489 it != user_callbacks_.end();
490 ++it) {
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)
502 : backend_(backend),
503 mru_cert_cache_(kMemoryCacheMaxSize),
504 mem_cache_hits_(0),
505 mem_cache_misses_(0),
506 weak_factory_(this) {
507 DCHECK(backend_);
510 DiskBasedCertCache::~DiskBasedCertCache() {
511 for (WriteWorkerMap::iterator it = write_worker_map_.begin();
512 it != write_worker_map_.end();
513 ++it) {
514 it->second->Cancel();
516 for (ReadWorkerMap::iterator it = read_worker_map_.begin();
517 it != read_worker_map_.end();
518 ++it) {
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);
533 ++mem_cache_hits_;
534 cb.Run(mru_it->second);
535 return;
537 ++mem_cache_misses_;
539 ReadWorkerMap::iterator it = read_worker_map_.find(key);
541 if (it == read_worker_map_.end()) {
542 ReadWorker* worker =
543 new ReadWorker(backend_,
544 key,
545 base::Bind(&DiskBasedCertCache::FinishedReadOperation,
546 weak_factory_.GetWeakPtr(),
547 key));
548 read_worker_map_[key] = worker;
549 worker->AddCallback(cb);
550 worker->Start();
551 } else {
552 it->second->AddCallback(cb);
556 void DiskBasedCertCache::SetCertificate(
557 const X509Certificate::OSCertHandle cert_handle,
558 const SetCallback& cb) {
559 DCHECK(!cb.is_null());
560 DCHECK(cert_handle);
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_,
568 key,
569 cert_handle,
570 base::Bind(&DiskBasedCertCache::FinishedWriteOperation,
571 weak_factory_.GetWeakPtr(),
572 key,
573 cert_handle));
574 write_worker_map_[key] = worker;
575 worker->AddCallback(cb);
576 worker->Start();
577 } else {
578 it->second->AddCallback(cb);
582 void DiskBasedCertCache::FinishedReadOperation(
583 const std::string& key,
584 X509Certificate::OSCertHandle cert_handle) {
585 if (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);
594 if (!key.empty())
595 mru_cert_cache_.Put(key, X509Certificate::DupOSCertHandle(cert_handle));
598 } // namespace net