Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(206)

Side by Side Diff: net/http/disk_based_cert_cache.cc

Issue 329733002: Disk Based Certificate Cache Implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Updated unittests with respect to the review of patch 9. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "net/http/disk_based_cert_cache.h"
6
7 #include <vector>
8
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"
17
18 namespace net {
19
20 namespace {
21
22 // Used to obtain a unique cache key for a certificate in the form of
23 // "cert:<hash>".
24 std::string GetCacheKeyToCert(const X509Certificate::OSCertHandle cert_handle) {
25 SHA1HashValue fingerprint =
26 X509Certificate::CalculateFingerprint(cert_handle);
27
28 return "cert:" +
29 base::HexEncode(fingerprint.data, arraysize(fingerprint.data));
30 }
31
32 } // namespace
33
34 // WriteWorkers represent pending Set jobs in the DiskBasedCertCache. Each
35 // certificate requested to be cached is assigned a Writeworker on a one-to-one
36 // basis. The same certificate should not have multiple WriteWorkers at the same
37 // time; instead, add a user callback to the existing WriteWorker.
38 class DiskBasedCertCache::WriteWorker {
39 public:
40 // |backend| is the backend to store |certificate| in, using
41 // |key| as the key for the disk_cache::Entry.
42 // |cleanup_callback| is called to clean up this ReadWorker,
43 // regardless of success or failure.
44 WriteWorker(disk_cache::Backend* backend,
45 const std::string& key,
46 X509Certificate::OSCertHandle cert_handle,
47 const base::Closure& cleanup_callback);
48
49 ~WriteWorker();
50
51 // Writes the given certificate to the cache. On completion, will invoke all
52 // user callbacks.
53 void Start();
54
55 // Adds a callback to the set of callbacks to be run when this
56 // WriteWorker finishes processing.
57 void AddCallback(const SetCallback& user_callback);
58
59 // Signals the WriteWorker to abort early. The WriteWorker will be destroyed
60 // upon the completion of any pending callbacks. User callbacks will be
61 // invoked with an empty string.
62 void Cancel();
63
64 private:
65 enum WriteState {
66 STATE_CREATE,
67 STATE_CREATE_COMPLETE,
68 STATE_OPEN,
69 STATE_OPEN_COMPLETE,
70 STATE_WRITE,
71 STATE_WRITE_COMPLETE,
72 STATE_NONE
73 };
74
75 void OnIOComplete(int rv);
76 int DoLoop(int rv);
77
78 int DoCreate();
79 int DoCreateComplete(int rv);
80 int DoOpen();
81 int DoOpenComplete(int rv);
82 int DoWrite();
83 int DoWriteComplete(int rv);
84
85 void Finish(int rv);
86
87 // Invokes all of the |user_callbacks_|
88 void CallCallbacks(int rv);
89
90 disk_cache::Backend* backend_;
91 const X509Certificate::OSCertHandle cert_handle_;
92 std::string key_;
93 bool canceled_;
94
95 disk_cache::Entry* entry_;
96 WriteState state_;
97 scoped_refptr<IOBuffer> buffer_;
98
99 base::Closure cleanup_callback_;
100 std::vector<SetCallback> user_callbacks_;
101 CompletionCallback io_callback_;
102
103 base::WeakPtrFactory<WriteWorker> weak_factory_;
104 };
105
106 DiskBasedCertCache::WriteWorker::WriteWorker(
107 disk_cache::Backend* backend,
108 const std::string& key,
109 X509Certificate::OSCertHandle cert_handle,
110 const base::Closure& cleanup_callback)
111 : backend_(backend),
112 cert_handle_(cert_handle),
113 key_(key),
114 canceled_(false),
115 entry_(NULL),
116 state_(STATE_NONE),
117 cleanup_callback_(cleanup_callback),
118 weak_factory_(this) {
119 io_callback_ =
120 base::Bind(&WriteWorker::OnIOComplete, weak_factory_.GetWeakPtr());
Ryan Sleevi 2014/06/23 22:43:58 In the new design, why would io_callback_ ever nee
121 }
122
123 void DiskBasedCertCache::WriteWorker::Start() {
124 state_ = STATE_CREATE;
Ryan Sleevi 2014/06/23 22:43:57 You should add a DCHECK_EQ(STATE_NONE, state_); T
125 int rv = DoLoop(OK);
126
127 if (rv == ERR_IO_PENDING)
128 return;
129
130 Finish(rv);
131 }
132
133 void DiskBasedCertCache::WriteWorker::AddCallback(
134 const SetCallback& user_callback) {
135 user_callbacks_.push_back(user_callback);
136 }
137
138 void DiskBasedCertCache::WriteWorker::OnIOComplete(int rv) {
139 if (canceled_) {
140 Finish(ERR_FAILED);
141 return;
142 }
143
144 rv = DoLoop(rv);
145
146 if (rv == ERR_IO_PENDING)
147 return;
148
149 Finish(rv);
150 }
151
152 int DiskBasedCertCache::WriteWorker::DoLoop(int rv) {
153 do {
154 switch (state_) {
155 case STATE_CREATE:
156 rv = DoCreate();
157 break;
158 case STATE_CREATE_COMPLETE:
159 rv = DoCreateComplete(rv);
160 break;
161 case STATE_OPEN:
162 rv = DoOpen();
163 break;
164 case STATE_OPEN_COMPLETE:
165 rv = DoOpenComplete(rv);
166 break;
167 case STATE_WRITE:
168 rv = DoWrite();
169 break;
170 case STATE_WRITE_COMPLETE:
171 rv = DoWriteComplete(rv);
172 break;
173 case STATE_NONE:
174 break;
175 }
176 } while (rv != ERR_IO_PENDING && state_ != STATE_NONE);
Ryan Sleevi 2014/06/23 22:43:58 In order to prevent endless loops, we typically in
177
178 return rv;
179 }
180
181 int DiskBasedCertCache::WriteWorker::DoCreate() {
182 state_ = STATE_CREATE_COMPLETE;
183
184 return backend_->CreateEntry(key_, &entry_, io_callback_);
185 }
186
187 int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv) {
188 if (rv < 0) {
189 state_ = STATE_OPEN;
Ryan Sleevi 2014/06/23 22:43:57 It would be good to add a comment about why you tr
190 return rv;
191 }
192 state_ = STATE_WRITE;
Ryan Sleevi 2014/06/23 22:43:57 We usually add a line break between 191 and 192
193 return OK;
194 }
195
196 int DiskBasedCertCache::WriteWorker::DoOpen() {
197 return backend_->OpenEntry(key_, &entry_, io_callback_);
198 }
199
200 int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv) {
201 if (rv < 0) {
202 state_ = STATE_NONE;
203 return rv;
204 }
205 state_ = STATE_WRITE;
206 return OK;
207 }
208
209 int DiskBasedCertCache::WriteWorker::DoWrite() {
210 std::string write_data;
211 bool encoded = X509Certificate::GetDEREncoded(cert_handle_, &write_data);
212
213 if (!encoded) {
214 state_ = STATE_NONE;
215 return ERR_FAILED;
216 }
217
218 buffer_ = new IOBuffer(write_data.size());
219 memcpy(buffer_->data(), write_data.data(), write_data.size());
220
221 state_ = STATE_WRITE_COMPLETE;
222
223 return entry_->WriteData(0 /* index */,
224 0 /* offset */,
225 buffer_,
226 write_data.size(),
227 io_callback_,
228 true /* truncate */);
229 }
230
231 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv) {
232 state_ = STATE_NONE;
233 return rv;
Ryan Sleevi 2014/06/23 22:43:58 I don't see any documentation that WriteData() wil
234 }
235
236 void DiskBasedCertCache::WriteWorker::CallCallbacks(int rv) {
Ryan Sleevi 2014/06/23 22:43:57 naming OCD: Since Run() is the method name on Call
237 std::string key;
238 if (rv >= 0)
239 key = key_;
240
241 for (std::vector<SetCallback>::iterator it = user_callbacks_.begin();
242 it != user_callbacks_.end();
243 ++it) {
244 it->Run(key);
245 it->Reset();
Ryan Sleevi 2014/06/23 22:43:58 You don't need to manually be calling Reset() on t
246 }
247 user_callbacks_.clear();
248 }
249
250 void DiskBasedCertCache::WriteWorker::Finish(int rv) {
251 cleanup_callback_.Run();
252 cleanup_callback_.Reset();
Ryan Sleevi 2014/06/23 22:43:57 So it's ok to use base::ResetAndReturn(&cleanup_ca
253 CallCallbacks(rv);
254 delete this;
255 }
256
257 void DiskBasedCertCache::WriteWorker::Cancel() {
258 canceled_ = true;
259 }
260
261 DiskBasedCertCache::WriteWorker::~WriteWorker() {
262 if (entry_)
263 entry_->Close();
264 }
265
266 // ReadWorkers represent pending Get jobs in the DiskBasedCertCache. Each
267 // certificate requested to be retrieved from the cache is assigned a ReadWorker
268 // on a one-to-one basis. The same |key| should not have multiple ReadWorkers
269 // at the same time; instead, call AddCallback to add a user_callback_ to
270 // the the existing ReadWorker.
271 class DiskBasedCertCache::ReadWorker {
272 public:
273 // |backend| is the backend to read |certificate| from, using
274 // |key| as the key for the disk_cache::Entry.
275 // |cleanup_callback| is called to clean up this ReadWorker,
276 // regardless of success or failure.
277 ReadWorker(disk_cache::Backend* backend,
278 const std::string& key,
279 const base::Closure& cleanup_callback);
280
281 ~ReadWorker();
282
283 // Reads the given certificate from the cache. On completion, will invoke all
284 // user callbacks.
285 void Start();
286
287 // Adds a callback to the set of callbacks to be run when this
288 // ReadWorker finishes processing.
289 void AddCallback(const GetCallback& user_callback);
290
291 // Signals the ReadWorker to abort early. The ReadWorker will be destroyed
292 // upon the completion of any pending callbacks. User callbacks will be
293 // invoked with a NULL cert handle.
294 void Cancel();
295
296 private:
297 enum ReadState {
298 STATE_OPEN,
299 STATE_OPEN_COMPLETE,
300 STATE_READ,
301 STATE_READ_COMPLETE,
302 STATE_NONE
303 };
304
305 void OnIOComplete(int rv);
306 int DoLoop(int rv);
307 int DoOpen();
308 int DoOpenComplete(int rv);
309 int DoRead();
310 int DoReadComplete(int rv);
311 void Finish(int rv);
312
313 // Invokes all of |user_callbacks_|
314 void CallCallbacks();
315
316 disk_cache::Backend* backend_;
317 X509Certificate::OSCertHandle cert_handle_;
318 std::string key_;
319 bool canceled_;
320
321 disk_cache::Entry* entry_;
322
323 ReadState state_;
324 int entry_size_;
325 scoped_refptr<IOBuffer> buffer_;
326
327 base::Closure cleanup_callback_;
328 std::vector<GetCallback> user_callbacks_;
329 CompletionCallback io_callback_;
330 base::WeakPtrFactory<ReadWorker> weak_factory_;
331 };
332
333 DiskBasedCertCache::ReadWorker::ReadWorker(
334 disk_cache::Backend* backend,
335 const std::string& key,
336 const base::Closure& cleanup_callback)
337 : backend_(backend),
338 cert_handle_(NULL),
339 key_(key),
340 canceled_(false),
341 entry_(NULL),
342 state_(STATE_NONE),
343 entry_size_(0),
344 cleanup_callback_(cleanup_callback),
345 weak_factory_(this) {
346 io_callback_ =
347 base::Bind(&ReadWorker::OnIOComplete, weak_factory_.GetWeakPtr());
348 }
349
350 void DiskBasedCertCache::ReadWorker::Start() {
351 state_ = STATE_OPEN;
352 int rv = DoLoop(OK);
353
354 if (rv == ERR_IO_PENDING)
355 return;
356
357 Finish(rv);
358 }
359
360 void DiskBasedCertCache::ReadWorker::AddCallback(
361 const GetCallback& user_callback) {
362 user_callbacks_.push_back(user_callback);
363 }
364
365 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv) {
366 if (canceled_) {
367 Finish(ERR_FAILED);
368 return;
369 }
370
371 rv = DoLoop(rv);
372
373 if (rv == ERR_IO_PENDING)
374 return;
375
376 Finish(rv);
377 }
378
379 int DiskBasedCertCache::ReadWorker::DoLoop(int rv) {
380 do {
381 switch (state_) {
382 case STATE_OPEN:
383 rv = DoOpen();
384 break;
385 case STATE_OPEN_COMPLETE:
386 rv = DoOpenComplete(rv);
387 break;
388 case STATE_READ:
389 rv = DoRead();
390 break;
391 case STATE_READ_COMPLETE:
392 rv = DoReadComplete(rv);
393 break;
394 case STATE_NONE:
395 break;
396 }
397 } while (rv != ERR_IO_PENDING && state_ != STATE_NONE);
398
399 return rv;
400 }
401
402 int DiskBasedCertCache::ReadWorker::DoOpen() {
403 state_ = STATE_OPEN_COMPLETE;
404 return backend_->OpenEntry(key_, &entry_, io_callback_);
405 }
406
407 int DiskBasedCertCache::ReadWorker::DoOpenComplete(int rv) {
408 if (rv < 0) {
409 state_ = STATE_NONE;
410 return rv;
411 }
412 state_ = STATE_READ;
413 return OK;
414 }
415
416 int DiskBasedCertCache::ReadWorker::DoRead() {
417 state_ = STATE_READ_COMPLETE;
418 entry_size_ = entry_->GetDataSize(0 /* index */);
419 buffer_ = new IOBuffer(entry_size_);
420 return entry_->ReadData(
421 0 /* index */, 0 /* offset */, buffer_, entry_size_, io_callback_);
422 }
423
424 int DiskBasedCertCache::ReadWorker::DoReadComplete(int rv) {
425 state_ = STATE_NONE;
426 if (rv < 0)
427 return rv;
428
429 cert_handle_ = X509Certificate::CreateOSCertHandleFromBytes(buffer_->data(),
430 entry_size_);
431
432 DCHECK(cert_handle_);
wtc 2014/06/24 00:21:04 Replace this DCHECK with error handling: if (!ce
433 return OK;
434 }
435
436 void DiskBasedCertCache::ReadWorker::CallCallbacks() {
437 for (std::vector<GetCallback>::iterator it = user_callbacks_.begin();
438 it != user_callbacks_.end();
439 ++it) {
440 it->Run(cert_handle_);
441 it->Reset();
442 }
443 user_callbacks_.clear();
444 }
445
446 void DiskBasedCertCache::ReadWorker::Finish(int rv) {
447 cleanup_callback_.Run();
448 cleanup_callback_.Reset();
449 CallCallbacks();
450
451 delete this;
452 }
453
454 void DiskBasedCertCache::ReadWorker::Cancel() {
455 canceled_ = true;
456 }
457
458 DiskBasedCertCache::ReadWorker::~ReadWorker() {
459 if (entry_)
460 entry_->Close();
461 if (cert_handle_)
462 X509Certificate::FreeOSCertHandle(cert_handle_);
463 }
464
465 DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend* backend)
466 : backend_(backend), weak_factory_(this) {
467 DCHECK(backend_);
468 }
469
470 DiskBasedCertCache::~DiskBasedCertCache() {
471 for (WriteWorkerMap::iterator it = write_worker_map_.begin();
472 it != write_worker_map_.end();
473 ++it) {
474 it->second->Cancel();
475 }
476 for (ReadWorkerMap::iterator it = read_worker_map_.begin();
477 it != read_worker_map_.end();
478 ++it) {
479 it->second->Cancel();
480 }
481 }
482
483 void DiskBasedCertCache::Get(const std::string& key, const GetCallback& cb) {
484 DCHECK(!key.empty());
485
486 ReadWorkerMap::iterator it = read_worker_map_.find(key);
487
488 if (it == read_worker_map_.end()) {
489 ReadWorker* worker =
490 new ReadWorker(backend_,
491 key,
492 base::Bind(&DiskBasedCertCache::FinishedReadOperation,
493 weak_factory_.GetWeakPtr(),
494 key));
495 read_worker_map_[key] = worker;
496 worker->AddCallback(cb);
497 worker->Start();
498 } else {
499 it->second->AddCallback(cb);
500 }
501 }
502
503 void DiskBasedCertCache::Set(const X509Certificate::OSCertHandle cert_handle,
504 const SetCallback& cb) {
505 DCHECK(!cb.is_null());
506 DCHECK(cert_handle);
507 std::string key = GetCacheKeyToCert(cert_handle);
508
509 WriteWorkerMap::iterator it = write_worker_map_.find(key);
510
511 if (it == write_worker_map_.end()) {
512 WriteWorker* worker =
513 new WriteWorker(backend_,
514 key,
515 cert_handle,
516 base::Bind(&DiskBasedCertCache::FinishedWriteOperation,
517 weak_factory_.GetWeakPtr(),
518 key));
519 write_worker_map_[key] = worker;
520 worker->AddCallback(cb);
521 worker->Start();
522 } else {
523 it->second->AddCallback(cb);
524 }
525 }
526
527 void DiskBasedCertCache::FinishedWriteOperation(const std::string& key) {
528 write_worker_map_.erase(key);
529 }
530
531 void DiskBasedCertCache::FinishedReadOperation(const std::string& key) {
532 read_worker_map_.erase(key);
533 }
534
535 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698