1 // Copyright 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/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 #include "net/disk_cache/disk_cache.h"
22 // TODO(brandonsalmon): change this number to improve performance.
23 const size_t kMemoryCacheMaxSize
= 30;
25 // Used to obtain a unique cache key for a certificate in the form of
27 std::string
GetCacheKeyToCert(const X509Certificate::OSCertHandle cert_handle
) {
28 SHA1HashValue fingerprint
=
29 X509Certificate::CalculateFingerprint(cert_handle
);
32 base::HexEncode(fingerprint
.data
, arraysize(fingerprint
.data
));
37 // WriteWorkers represent pending Set jobs in the DiskBasedCertCache. Each
38 // certificate requested to be cached is assigned a Writeworker on a one-to-one
39 // basis. The same certificate should not have multiple WriteWorkers at the same
40 // time; instead, add a user callback to the existing WriteWorker.
41 class DiskBasedCertCache::WriteWorker
{
43 // |backend| is the backend to store |certificate| in, using
44 // |key| as the key for the disk_cache::Entry.
45 // |cleanup_callback| is called to clean up this ReadWorker,
46 // regardless of success or failure.
47 WriteWorker(disk_cache::Backend
* backend
,
48 const std::string
& key
,
49 X509Certificate::OSCertHandle cert_handle
,
50 const base::Closure
& cleanup_callback
);
54 // Writes the given certificate to the cache. On completion, will invoke all
58 // Adds a callback to the set of callbacks to be run when this
59 // WriteWorker finishes processing.
60 void AddCallback(const SetCallback
& user_callback
);
62 // Signals the WriteWorker to abort early. The WriteWorker will be destroyed
63 // upon the completion of any pending callbacks. User callbacks will be
64 // invoked with an empty string.
70 STATE_CREATE_COMPLETE
,
78 void OnIOComplete(int rv
);
82 int DoCreateComplete(int rv
);
84 int DoOpenComplete(int rv
);
86 int DoWriteComplete(int rv
);
90 // Invokes all of the |user_callbacks_|
91 void RunCallbacks(int rv
);
93 disk_cache::Backend
* backend_
;
94 const X509Certificate::OSCertHandle cert_handle_
;
98 disk_cache::Entry
* entry_
;
100 scoped_refptr
<IOBuffer
> buffer_
;
103 base::Closure cleanup_callback_
;
104 std::vector
<SetCallback
> user_callbacks_
;
105 CompletionCallback io_callback_
;
108 DiskBasedCertCache::WriteWorker::WriteWorker(
109 disk_cache::Backend
* backend
,
110 const std::string
& key
,
111 X509Certificate::OSCertHandle cert_handle
,
112 const base::Closure
& cleanup_callback
)
114 cert_handle_(X509Certificate::DupOSCertHandle(cert_handle
)),
120 cleanup_callback_(cleanup_callback
),
122 base::Bind(&WriteWorker::OnIOComplete
, base::Unretained(this))) {
125 DiskBasedCertCache::WriteWorker::~WriteWorker() {
126 X509Certificate::FreeOSCertHandle(cert_handle_
);
131 void DiskBasedCertCache::WriteWorker::Start() {
132 DCHECK_EQ(STATE_NONE
, state_
);
133 state_
= STATE_CREATE
;
136 if (rv
== ERR_IO_PENDING
)
142 void DiskBasedCertCache::WriteWorker::AddCallback(
143 const SetCallback
& user_callback
) {
144 user_callbacks_
.push_back(user_callback
);
147 void DiskBasedCertCache::WriteWorker::Cancel() {
151 void DiskBasedCertCache::WriteWorker::OnIOComplete(int rv
) {
159 if (rv
== ERR_IO_PENDING
)
165 int DiskBasedCertCache::WriteWorker::DoLoop(int rv
) {
167 State next_state
= state_
;
169 switch (next_state
) {
173 case STATE_CREATE_COMPLETE
:
174 rv
= DoCreateComplete(rv
);
179 case STATE_OPEN_COMPLETE
:
180 rv
= DoOpenComplete(rv
);
185 case STATE_WRITE_COMPLETE
:
186 rv
= DoWriteComplete(rv
);
192 } while (rv
!= ERR_IO_PENDING
&& state_
!= STATE_NONE
);
197 int DiskBasedCertCache::WriteWorker::DoCreate() {
198 state_
= STATE_CREATE_COMPLETE
;
200 return backend_
->CreateEntry(key_
, &entry_
, io_callback_
);
203 int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv
) {
204 // An error here usually signifies that the entry already exists.
205 // If this occurs, it is necessary to instead open the previously
212 state_
= STATE_WRITE
;
216 int DiskBasedCertCache::WriteWorker::DoOpen() {
217 state_
= STATE_OPEN_COMPLETE
;
218 return backend_
->OpenEntry(key_
, &entry_
, io_callback_
);
221 int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv
) {
225 state_
= STATE_WRITE
;
229 int DiskBasedCertCache::WriteWorker::DoWrite() {
230 std::string write_data
;
231 bool encoded
= X509Certificate::GetDEREncoded(cert_handle_
, &write_data
);
236 buffer_
= new IOBuffer(write_data
.size());
237 io_buf_len_
= write_data
.size();
238 memcpy(buffer_
->data(), write_data
.data(), io_buf_len_
);
240 state_
= STATE_WRITE_COMPLETE
;
242 return entry_
->WriteData(0 /* index */,
247 true /* truncate */);
250 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv
) {
251 if (rv
< io_buf_len_
)
257 void DiskBasedCertCache::WriteWorker::Finish(int rv
) {
258 cleanup_callback_
.Run();
259 cleanup_callback_
.Reset();
264 void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv
) {
269 for (std::vector
<SetCallback
>::const_iterator it
= user_callbacks_
.begin();
270 it
!= user_callbacks_
.end();
274 user_callbacks_
.clear();
277 // ReadWorkers represent pending Get jobs in the DiskBasedCertCache. Each
278 // certificate requested to be retrieved from the cache is assigned a ReadWorker
279 // on a one-to-one basis. The same |key| should not have multiple ReadWorkers
280 // at the same time; instead, call AddCallback to add a user_callback_ to
281 // the the existing ReadWorker.
282 class DiskBasedCertCache::ReadWorker
{
284 // |backend| is the backend to read |certificate| from, using
285 // |key| as the key for the disk_cache::Entry.
286 // |cleanup_callback| is called to clean up this ReadWorker,
287 // regardless of success or failure.
288 ReadWorker(disk_cache::Backend
* backend
,
289 const std::string
& key
,
290 const GetCallback
& cleanup_callback
);
294 // Reads the given certificate from the cache. On completion, will invoke all
298 // Adds a callback to the set of callbacks to be run when this
299 // ReadWorker finishes processing.
300 void AddCallback(const GetCallback
& user_callback
);
302 // Signals the ReadWorker to abort early. The ReadWorker will be destroyed
303 // upon the completion of any pending callbacks. User callbacks will be
304 // invoked with a NULL cert handle.
316 void OnIOComplete(int rv
);
319 int DoOpenComplete(int rv
);
321 int DoReadComplete(int rv
);
324 // Invokes all of |user_callbacks_|
327 disk_cache::Backend
* backend_
;
328 X509Certificate::OSCertHandle cert_handle_
;
332 disk_cache::Entry
* entry_
;
335 scoped_refptr
<IOBuffer
> buffer_
;
338 GetCallback cleanup_callback_
;
339 std::vector
<GetCallback
> user_callbacks_
;
340 CompletionCallback io_callback_
;
343 DiskBasedCertCache::ReadWorker::ReadWorker(disk_cache::Backend
* backend
,
344 const std::string
& key
,
345 const GetCallback
& cleanup_callback
)
353 cleanup_callback_(cleanup_callback
),
355 base::Bind(&ReadWorker::OnIOComplete
, base::Unretained(this))) {
358 DiskBasedCertCache::ReadWorker::~ReadWorker() {
362 X509Certificate::FreeOSCertHandle(cert_handle_
);
365 void DiskBasedCertCache::ReadWorker::Start() {
366 DCHECK_EQ(STATE_NONE
, state_
);
370 if (rv
== ERR_IO_PENDING
)
376 void DiskBasedCertCache::ReadWorker::AddCallback(
377 const GetCallback
& user_callback
) {
378 user_callbacks_
.push_back(user_callback
);
381 void DiskBasedCertCache::ReadWorker::Cancel() {
385 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv
) {
393 if (rv
== ERR_IO_PENDING
)
399 int DiskBasedCertCache::ReadWorker::DoLoop(int rv
) {
401 State next_state
= state_
;
403 switch (next_state
) {
407 case STATE_OPEN_COMPLETE
:
408 rv
= DoOpenComplete(rv
);
413 case STATE_READ_COMPLETE
:
414 rv
= DoReadComplete(rv
);
420 } while (rv
!= ERR_IO_PENDING
&& state_
!= STATE_NONE
);
425 int DiskBasedCertCache::ReadWorker::DoOpen() {
426 state_
= STATE_OPEN_COMPLETE
;
427 return backend_
->OpenEntry(key_
, &entry_
, io_callback_
);
430 int DiskBasedCertCache::ReadWorker::DoOpenComplete(int rv
) {
438 int DiskBasedCertCache::ReadWorker::DoRead() {
439 state_
= STATE_READ_COMPLETE
;
440 io_buf_len_
= entry_
->GetDataSize(0 /* index */);
441 buffer_
= new IOBuffer(io_buf_len_
);
442 return entry_
->ReadData(
443 0 /* index */, 0 /* offset */, buffer_
, io_buf_len_
, io_callback_
);
446 int DiskBasedCertCache::ReadWorker::DoReadComplete(int rv
) {
447 if (rv
< io_buf_len_
)
450 cert_handle_
= X509Certificate::CreateOSCertHandleFromBytes(buffer_
->data(),
458 void DiskBasedCertCache::ReadWorker::Finish(int rv
) {
459 cleanup_callback_
.Run(cert_handle_
);
460 cleanup_callback_
.Reset();
465 void DiskBasedCertCache::ReadWorker::RunCallbacks() {
466 for (std::vector
<GetCallback
>::const_iterator it
= user_callbacks_
.begin();
467 it
!= user_callbacks_
.end();
469 it
->Run(cert_handle_
);
471 user_callbacks_
.clear();
474 void DiskBasedCertCache::CertFree::operator()(
475 X509Certificate::OSCertHandle cert_handle
) {
476 X509Certificate::FreeOSCertHandle(cert_handle
);
479 DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend
* backend
)
481 mru_cert_cache_(kMemoryCacheMaxSize
),
483 mem_cache_misses_(0),
484 weak_factory_(this) {
488 DiskBasedCertCache::~DiskBasedCertCache() {
489 for (WriteWorkerMap::iterator it
= write_worker_map_
.begin();
490 it
!= write_worker_map_
.end();
492 it
->second
->Cancel();
494 for (ReadWorkerMap::iterator it
= read_worker_map_
.begin();
495 it
!= read_worker_map_
.end();
497 it
->second
->Cancel();
501 void DiskBasedCertCache::Get(const std::string
& key
, const GetCallback
& cb
) {
502 DCHECK(!key
.empty());
504 // If the handle is already in the MRU cache, just return that (via callback).
505 // Note, this will also bring the cert_handle to the front of the recency
506 // list in the MRU cache.
507 MRUCertCache::iterator mru_it
= mru_cert_cache_
.Get(key
);
508 if (mru_it
!= mru_cert_cache_
.end()) {
510 cb
.Run(mru_it
->second
);
515 ReadWorkerMap::iterator it
= read_worker_map_
.find(key
);
517 if (it
== read_worker_map_
.end()) {
519 new ReadWorker(backend_
,
521 base::Bind(&DiskBasedCertCache::FinishedReadOperation
,
522 weak_factory_
.GetWeakPtr(),
524 read_worker_map_
[key
] = worker
;
525 worker
->AddCallback(cb
);
528 it
->second
->AddCallback(cb
);
532 void DiskBasedCertCache::Set(const X509Certificate::OSCertHandle cert_handle
,
533 const SetCallback
& cb
) {
534 DCHECK(!cb
.is_null());
536 std::string key
= GetCacheKeyToCert(cert_handle
);
538 WriteWorkerMap::iterator it
= write_worker_map_
.find(key
);
540 if (it
== write_worker_map_
.end()) {
541 WriteWorker
* worker
=
542 new WriteWorker(backend_
,
545 base::Bind(&DiskBasedCertCache::FinishedWriteOperation
,
546 weak_factory_
.GetWeakPtr(),
549 write_worker_map_
[key
] = worker
;
550 worker
->AddCallback(cb
);
553 it
->second
->AddCallback(cb
);
557 void DiskBasedCertCache::FinishedReadOperation(
558 const std::string
& key
,
559 X509Certificate::OSCertHandle cert_handle
) {
561 mru_cert_cache_
.Put(key
, X509Certificate::DupOSCertHandle(cert_handle
));
562 read_worker_map_
.erase(key
);
565 void DiskBasedCertCache::FinishedWriteOperation(
566 const std::string
& key
,
567 X509Certificate::OSCertHandle cert_handle
) {
568 write_worker_map_
.erase(key
);
570 mru_cert_cache_
.Put(key
, X509Certificate::DupOSCertHandle(cert_handle
));