Probably broke Win7 Tests (dbg)(6). http://build.chromium.org/p/chromium.win/builders...
[chromium-blink-merge.git] / net / http / disk_based_cert_cache.cc
blob132648c0b25c0764f388618f397d000ca4a9a22f
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"
7 #include <vector>
9 #include "base/bind.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"
18 namespace net {
20 namespace {
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
26 // "cert:<hash>".
27 std::string GetCacheKeyToCert(const X509Certificate::OSCertHandle cert_handle) {
28 SHA1HashValue fingerprint =
29 X509Certificate::CalculateFingerprint(cert_handle);
31 return "cert:" +
32 base::HexEncode(fingerprint.data, arraysize(fingerprint.data));
35 } // namespace
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 {
42 public:
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);
52 ~WriteWorker();
54 // Writes the given certificate to the cache. On completion, will invoke all
55 // user callbacks.
56 void Start();
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.
65 void Cancel();
67 private:
68 enum State {
69 STATE_CREATE,
70 STATE_CREATE_COMPLETE,
71 STATE_OPEN,
72 STATE_OPEN_COMPLETE,
73 STATE_WRITE,
74 STATE_WRITE_COMPLETE,
75 STATE_NONE
78 void OnIOComplete(int rv);
79 int DoLoop(int rv);
81 int DoCreate();
82 int DoCreateComplete(int rv);
83 int DoOpen();
84 int DoOpenComplete(int rv);
85 int DoWrite();
86 int DoWriteComplete(int rv);
88 void Finish(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_;
95 std::string key_;
96 bool canceled_;
98 disk_cache::Entry* entry_;
99 State state_;
100 scoped_refptr<IOBuffer> buffer_;
101 int io_buf_len_;
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)
113 : backend_(backend),
114 cert_handle_(X509Certificate::DupOSCertHandle(cert_handle)),
115 key_(key),
116 canceled_(false),
117 entry_(NULL),
118 state_(STATE_NONE),
119 io_buf_len_(0),
120 cleanup_callback_(cleanup_callback),
121 io_callback_(
122 base::Bind(&WriteWorker::OnIOComplete, base::Unretained(this))) {
125 DiskBasedCertCache::WriteWorker::~WriteWorker() {
126 X509Certificate::FreeOSCertHandle(cert_handle_);
127 if (entry_)
128 entry_->Close();
131 void DiskBasedCertCache::WriteWorker::Start() {
132 DCHECK_EQ(STATE_NONE, state_);
133 state_ = STATE_CREATE;
134 int rv = DoLoop(OK);
136 if (rv == ERR_IO_PENDING)
137 return;
139 Finish(rv);
142 void DiskBasedCertCache::WriteWorker::AddCallback(
143 const SetCallback& user_callback) {
144 user_callbacks_.push_back(user_callback);
147 void DiskBasedCertCache::WriteWorker::Cancel() {
148 canceled_ = true;
151 void DiskBasedCertCache::WriteWorker::OnIOComplete(int rv) {
152 if (canceled_) {
153 Finish(ERR_FAILED);
154 return;
157 rv = DoLoop(rv);
159 if (rv == ERR_IO_PENDING)
160 return;
162 Finish(rv);
165 int DiskBasedCertCache::WriteWorker::DoLoop(int rv) {
166 do {
167 State next_state = state_;
168 state_ = STATE_NONE;
169 switch (next_state) {
170 case STATE_CREATE:
171 rv = DoCreate();
172 break;
173 case STATE_CREATE_COMPLETE:
174 rv = DoCreateComplete(rv);
175 break;
176 case STATE_OPEN:
177 rv = DoOpen();
178 break;
179 case STATE_OPEN_COMPLETE:
180 rv = DoOpenComplete(rv);
181 break;
182 case STATE_WRITE:
183 rv = DoWrite();
184 break;
185 case STATE_WRITE_COMPLETE:
186 rv = DoWriteComplete(rv);
187 break;
188 case STATE_NONE:
189 NOTREACHED();
190 break;
192 } while (rv != ERR_IO_PENDING && state_ != STATE_NONE);
194 return rv;
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
206 // existing entry.
207 if (rv < 0) {
208 state_ = STATE_OPEN;
209 return OK;
212 state_ = STATE_WRITE;
213 return OK;
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) {
222 if (rv < 0)
223 return rv;
225 state_ = STATE_WRITE;
226 return OK;
229 int DiskBasedCertCache::WriteWorker::DoWrite() {
230 std::string write_data;
231 bool encoded = X509Certificate::GetDEREncoded(cert_handle_, &write_data);
233 if (!encoded)
234 return ERR_FAILED;
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 */,
243 0 /* offset */,
244 buffer_,
245 write_data.size(),
246 io_callback_,
247 true /* truncate */);
250 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv) {
251 if (rv < io_buf_len_)
252 return ERR_FAILED;
254 return OK;
257 void DiskBasedCertCache::WriteWorker::Finish(int rv) {
258 cleanup_callback_.Run();
259 cleanup_callback_.Reset();
260 RunCallbacks(rv);
261 delete this;
264 void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv) {
265 std::string key;
266 if (rv >= 0)
267 key = key_;
269 for (std::vector<SetCallback>::const_iterator it = user_callbacks_.begin();
270 it != user_callbacks_.end();
271 ++it) {
272 it->Run(key);
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 {
283 public:
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);
292 ~ReadWorker();
294 // Reads the given certificate from the cache. On completion, will invoke all
295 // user callbacks.
296 void Start();
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.
305 void Cancel();
307 private:
308 enum State {
309 STATE_OPEN,
310 STATE_OPEN_COMPLETE,
311 STATE_READ,
312 STATE_READ_COMPLETE,
313 STATE_NONE
316 void OnIOComplete(int rv);
317 int DoLoop(int rv);
318 int DoOpen();
319 int DoOpenComplete(int rv);
320 int DoRead();
321 int DoReadComplete(int rv);
322 void Finish(int rv);
324 // Invokes all of |user_callbacks_|
325 void RunCallbacks();
327 disk_cache::Backend* backend_;
328 X509Certificate::OSCertHandle cert_handle_;
329 std::string key_;
330 bool canceled_;
332 disk_cache::Entry* entry_;
334 State state_;
335 scoped_refptr<IOBuffer> buffer_;
336 int io_buf_len_;
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)
346 : backend_(backend),
347 cert_handle_(NULL),
348 key_(key),
349 canceled_(false),
350 entry_(NULL),
351 state_(STATE_NONE),
352 io_buf_len_(0),
353 cleanup_callback_(cleanup_callback),
354 io_callback_(
355 base::Bind(&ReadWorker::OnIOComplete, base::Unretained(this))) {
358 DiskBasedCertCache::ReadWorker::~ReadWorker() {
359 if (entry_)
360 entry_->Close();
361 if (cert_handle_)
362 X509Certificate::FreeOSCertHandle(cert_handle_);
365 void DiskBasedCertCache::ReadWorker::Start() {
366 DCHECK_EQ(STATE_NONE, state_);
367 state_ = STATE_OPEN;
368 int rv = DoLoop(OK);
370 if (rv == ERR_IO_PENDING)
371 return;
373 Finish(rv);
376 void DiskBasedCertCache::ReadWorker::AddCallback(
377 const GetCallback& user_callback) {
378 user_callbacks_.push_back(user_callback);
381 void DiskBasedCertCache::ReadWorker::Cancel() {
382 canceled_ = true;
385 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv) {
386 if (canceled_) {
387 Finish(ERR_FAILED);
388 return;
391 rv = DoLoop(rv);
393 if (rv == ERR_IO_PENDING)
394 return;
396 Finish(rv);
399 int DiskBasedCertCache::ReadWorker::DoLoop(int rv) {
400 do {
401 State next_state = state_;
402 state_ = STATE_NONE;
403 switch (next_state) {
404 case STATE_OPEN:
405 rv = DoOpen();
406 break;
407 case STATE_OPEN_COMPLETE:
408 rv = DoOpenComplete(rv);
409 break;
410 case STATE_READ:
411 rv = DoRead();
412 break;
413 case STATE_READ_COMPLETE:
414 rv = DoReadComplete(rv);
415 break;
416 case STATE_NONE:
417 NOTREACHED();
418 break;
420 } while (rv != ERR_IO_PENDING && state_ != STATE_NONE);
422 return rv;
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) {
431 if (rv < 0)
432 return rv;
434 state_ = STATE_READ;
435 return OK;
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_)
448 return ERR_FAILED;
450 cert_handle_ = X509Certificate::CreateOSCertHandleFromBytes(buffer_->data(),
451 io_buf_len_);
452 if (!cert_handle_)
453 return ERR_FAILED;
455 return OK;
458 void DiskBasedCertCache::ReadWorker::Finish(int rv) {
459 cleanup_callback_.Run(cert_handle_);
460 cleanup_callback_.Reset();
461 RunCallbacks();
462 delete this;
465 void DiskBasedCertCache::ReadWorker::RunCallbacks() {
466 for (std::vector<GetCallback>::const_iterator it = user_callbacks_.begin();
467 it != user_callbacks_.end();
468 ++it) {
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)
480 : backend_(backend),
481 mru_cert_cache_(kMemoryCacheMaxSize),
482 mem_cache_hits_(0),
483 mem_cache_misses_(0),
484 weak_factory_(this) {
485 DCHECK(backend_);
488 DiskBasedCertCache::~DiskBasedCertCache() {
489 for (WriteWorkerMap::iterator it = write_worker_map_.begin();
490 it != write_worker_map_.end();
491 ++it) {
492 it->second->Cancel();
494 for (ReadWorkerMap::iterator it = read_worker_map_.begin();
495 it != read_worker_map_.end();
496 ++it) {
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()) {
509 ++mem_cache_hits_;
510 cb.Run(mru_it->second);
511 return;
513 ++mem_cache_misses_;
515 ReadWorkerMap::iterator it = read_worker_map_.find(key);
517 if (it == read_worker_map_.end()) {
518 ReadWorker* worker =
519 new ReadWorker(backend_,
520 key,
521 base::Bind(&DiskBasedCertCache::FinishedReadOperation,
522 weak_factory_.GetWeakPtr(),
523 key));
524 read_worker_map_[key] = worker;
525 worker->AddCallback(cb);
526 worker->Start();
527 } else {
528 it->second->AddCallback(cb);
532 void DiskBasedCertCache::Set(const X509Certificate::OSCertHandle cert_handle,
533 const SetCallback& cb) {
534 DCHECK(!cb.is_null());
535 DCHECK(cert_handle);
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_,
543 key,
544 cert_handle,
545 base::Bind(&DiskBasedCertCache::FinishedWriteOperation,
546 weak_factory_.GetWeakPtr(),
547 key,
548 cert_handle));
549 write_worker_map_[key] = worker;
550 worker->AddCallback(cb);
551 worker->Start();
552 } else {
553 it->second->AddCallback(cb);
557 void DiskBasedCertCache::FinishedReadOperation(
558 const std::string& key,
559 X509Certificate::OSCertHandle cert_handle) {
560 if (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);
569 if (!key.empty())
570 mru_cert_cache_.Put(key, X509Certificate::DupOSCertHandle(cert_handle));
573 } // namespace net