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

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 and improved unittests. Created 6 years, 5 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 State {
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 RunCallbacks(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 State state_;
97 scoped_refptr<IOBuffer> buffer_;
98 int io_buf_len_;
99
100 base::Closure cleanup_callback_;
101 std::vector<SetCallback> user_callbacks_;
102 CompletionCallback io_callback_;
103 };
104
105 DiskBasedCertCache::WriteWorker::WriteWorker(
106 disk_cache::Backend* backend,
107 const std::string& key,
108 X509Certificate::OSCertHandle cert_handle,
109 const base::Closure& cleanup_callback)
110 : backend_(backend),
111 cert_handle_(cert_handle),
112 key_(key),
113 canceled_(false),
114 entry_(NULL),
115 state_(STATE_NONE),
116 io_buf_len_(0),
117 cleanup_callback_(cleanup_callback),
118 io_callback_(
119 base::Bind(&WriteWorker::OnIOComplete, base::Unretained(this))) {
120 }
121
122 void DiskBasedCertCache::WriteWorker::Start() {
123 DCHECK_EQ(STATE_NONE, state_);
124 state_ = STATE_CREATE;
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 State next_state = state_;
155 state_ = STATE_NONE;
156 switch (next_state) {
157 case STATE_CREATE:
158 rv = DoCreate();
159 break;
160 case STATE_CREATE_COMPLETE:
161 rv = DoCreateComplete(rv);
162 break;
163 case STATE_OPEN:
164 rv = DoOpen();
165 break;
166 case STATE_OPEN_COMPLETE:
167 rv = DoOpenComplete(rv);
168 break;
169 case STATE_WRITE:
170 rv = DoWrite();
171 break;
172 case STATE_WRITE_COMPLETE:
173 rv = DoWriteComplete(rv);
174 break;
175 case STATE_NONE:
176 NOTREACHED();
177 break;
178 }
179 } while (rv != ERR_IO_PENDING && state_ != STATE_NONE);
180 return rv;
181 }
182
183 int DiskBasedCertCache::WriteWorker::DoCreate() {
184 state_ = STATE_CREATE_COMPLETE;
185
186 return backend_->CreateEntry(key_, &entry_, io_callback_);
187 }
188
189 int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv) {
190 // An error here usually signifies that the entry already exists.
191 // If this occurs, it is necessary to instead open the previously
192 // existing entry.
193 if (rv < 0) {
194 state_ = STATE_OPEN;
195 return OK;
196 }
197
198 state_ = STATE_WRITE;
199 return OK;
200 }
201
202 int DiskBasedCertCache::WriteWorker::DoOpen() {
203 return backend_->OpenEntry(key_, &entry_, io_callback_);
204 }
205
206 int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv) {
207 if (rv < 0) {
208 state_ = STATE_NONE;
209 return rv;
210 }
211 state_ = STATE_WRITE;
212 return OK;
213 }
214
215 int DiskBasedCertCache::WriteWorker::DoWrite() {
216 std::string write_data;
217 bool encoded = X509Certificate::GetDEREncoded(cert_handle_, &write_data);
218 if (!encoded) {
219 state_ = STATE_NONE;
220 return ERR_FAILED;
221 }
222
223 buffer_ = new IOBuffer(write_data.size());
224 io_buf_len_ = write_data.size();
225 memcpy(buffer_->data(), write_data.data(), io_buf_len_);
226
227 state_ = STATE_WRITE_COMPLETE;
228
229 return entry_->WriteData(0 /* index */,
230 0 /* offset */,
231 buffer_,
232 write_data.size(),
233 io_callback_,
234 true /* truncate */);
235 }
236
237 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv) {
238 state_ = STATE_NONE;
239 if (rv < io_buf_len_)
240 return ERR_FAILED;
241
242 return OK;
243 }
244
245 void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv) {
246 std::string key;
247 if (rv >= 0)
248 key = key_;
249
250 for (std::vector<SetCallback>::const_iterator it = user_callbacks_.begin();
251 it != user_callbacks_.end();
252 ++it) {
253 it->Run(key);
254 }
255 user_callbacks_.clear();
256 }
257
258 void DiskBasedCertCache::WriteWorker::Finish(int rv) {
259 cleanup_callback_.Run();
260 cleanup_callback_.Reset();
261 RunCallbacks(rv);
262 delete this;
263 }
264
265 void DiskBasedCertCache::WriteWorker::Cancel() {
266 canceled_ = true;
267 }
268
269 DiskBasedCertCache::WriteWorker::~WriteWorker() {
270 if (entry_)
271 entry_->Close();
272 }
273
274 // ReadWorkers represent pending Get jobs in the DiskBasedCertCache. Each
275 // certificate requested to be retrieved from the cache is assigned a ReadWorker
276 // on a one-to-one basis. The same |key| should not have multiple ReadWorkers
277 // at the same time; instead, call AddCallback to add a user_callback_ to
278 // the the existing ReadWorker.
279 class DiskBasedCertCache::ReadWorker {
280 public:
281 // |backend| is the backend to read |certificate| from, using
282 // |key| as the key for the disk_cache::Entry.
283 // |cleanup_callback| is called to clean up this ReadWorker,
284 // regardless of success or failure.
285 ReadWorker(disk_cache::Backend* backend,
286 const std::string& key,
287 const base::Closure& cleanup_callback);
288
289 ~ReadWorker();
290
291 // Reads the given certificate from the cache. On completion, will invoke all
292 // user callbacks.
293 void Start();
294
295 // Adds a callback to the set of callbacks to be run when this
296 // ReadWorker finishes processing.
297 void AddCallback(const GetCallback& user_callback);
298
299 // Signals the ReadWorker to abort early. The ReadWorker will be destroyed
300 // upon the completion of any pending callbacks. User callbacks will be
301 // invoked with a NULL cert handle.
302 void Cancel();
303
304 private:
305 enum State {
306 STATE_OPEN,
307 STATE_OPEN_COMPLETE,
308 STATE_READ,
309 STATE_READ_COMPLETE,
310 STATE_NONE
311 };
312
313 void OnIOComplete(int rv);
314 int DoLoop(int rv);
315 int DoOpen();
316 int DoOpenComplete(int rv);
317 int DoRead();
318 int DoReadComplete(int rv);
319 void Finish(int rv);
320
321 // Invokes all of |user_callbacks_|
322 void RunCallbacks();
323
324 disk_cache::Backend* backend_;
325 X509Certificate::OSCertHandle cert_handle_;
326 std::string key_;
327 bool canceled_;
328
329 disk_cache::Entry* entry_;
330
331 State state_;
332 scoped_refptr<IOBuffer> buffer_;
333 int io_buf_len_;
334
335 base::Closure cleanup_callback_;
336 std::vector<GetCallback> user_callbacks_;
337 CompletionCallback io_callback_;
338 };
339
340 DiskBasedCertCache::ReadWorker::ReadWorker(
341 disk_cache::Backend* backend,
342 const std::string& key,
343 const base::Closure& cleanup_callback)
344 : backend_(backend),
345 cert_handle_(NULL),
346 key_(key),
347 canceled_(false),
348 entry_(NULL),
349 state_(STATE_NONE),
350 io_buf_len_(0),
351 cleanup_callback_(cleanup_callback),
352 io_callback_(
353 base::Bind(&ReadWorker::OnIOComplete, base::Unretained(this))) {
354 }
355
356 void DiskBasedCertCache::ReadWorker::Start() {
357 DCHECK_EQ(STATE_NONE, state_);
358 state_ = STATE_OPEN;
359 int rv = DoLoop(OK);
360
361 if (rv == ERR_IO_PENDING)
362 return;
363
364 Finish(rv);
365 }
366
367 void DiskBasedCertCache::ReadWorker::AddCallback(
368 const GetCallback& user_callback) {
369 user_callbacks_.push_back(user_callback);
370 }
371
372 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv) {
373 if (canceled_) {
374 Finish(ERR_FAILED);
375 return;
376 }
377
378 rv = DoLoop(rv);
379
380 if (rv == ERR_IO_PENDING)
381 return;
382
383 Finish(rv);
384 }
385
386 int DiskBasedCertCache::ReadWorker::DoLoop(int rv) {
387 do {
388 State next_state = state_;
389 state_ = STATE_NONE;
390 switch (next_state) {
391 case STATE_OPEN:
392 rv = DoOpen();
393 break;
394 case STATE_OPEN_COMPLETE:
395 rv = DoOpenComplete(rv);
396 break;
397 case STATE_READ:
398 rv = DoRead();
399 break;
400 case STATE_READ_COMPLETE:
401 rv = DoReadComplete(rv);
402 break;
403 case STATE_NONE:
404 NOTREACHED();
405 break;
406 }
407 } while (rv != ERR_IO_PENDING && state_ != STATE_NONE);
408 return rv;
409 }
410
411 int DiskBasedCertCache::ReadWorker::DoOpen() {
412 state_ = STATE_OPEN_COMPLETE;
413 return backend_->OpenEntry(key_, &entry_, io_callback_);
414 }
415
416 int DiskBasedCertCache::ReadWorker::DoOpenComplete(int rv) {
417 if (rv < 0) {
418 state_ = STATE_NONE;
419 return rv;
420 }
421 state_ = STATE_READ;
422 return OK;
423 }
424
425 int DiskBasedCertCache::ReadWorker::DoRead() {
426 state_ = STATE_READ_COMPLETE;
427 io_buf_len_ = entry_->GetDataSize(0 /* index */);
428 buffer_ = new IOBuffer(io_buf_len_);
429 return entry_->ReadData(
430 0 /* index */, 0 /* offset */, buffer_, io_buf_len_, io_callback_);
431 }
432
433 int DiskBasedCertCache::ReadWorker::DoReadComplete(int rv) {
434 state_ = STATE_NONE;
435 if (rv < io_buf_len_)
436 return ERR_FAILED;
437
438 cert_handle_ = X509Certificate::CreateOSCertHandleFromBytes(buffer_->data(),
439 io_buf_len_);
440 if (!cert_handle_)
441 return ERR_FAILED;
442
443 return OK;
444 }
445
446 void DiskBasedCertCache::ReadWorker::RunCallbacks() {
447 for (std::vector<GetCallback>::const_iterator it = user_callbacks_.begin();
448 it != user_callbacks_.end();
449 ++it) {
450 it->Run(cert_handle_);
451 }
452 user_callbacks_.clear();
453 }
454
455 void DiskBasedCertCache::ReadWorker::Finish(int rv) {
456 cleanup_callback_.Run();
457 cleanup_callback_.Reset();
458 RunCallbacks();
459 delete this;
460 }
461
462 void DiskBasedCertCache::ReadWorker::Cancel() {
463 canceled_ = true;
464 }
465
466 DiskBasedCertCache::ReadWorker::~ReadWorker() {
467 if (entry_)
468 entry_->Close();
469 if (cert_handle_)
470 X509Certificate::FreeOSCertHandle(cert_handle_);
471 }
472
473 DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend* backend)
474 : backend_(backend), weak_factory_(this) {
475 DCHECK(backend_);
476 }
477
478 DiskBasedCertCache::~DiskBasedCertCache() {
479 for (WriteWorkerMap::iterator it = write_worker_map_.begin();
480 it != write_worker_map_.end();
481 ++it) {
482 it->second->Cancel();
483 }
484 for (ReadWorkerMap::iterator it = read_worker_map_.begin();
485 it != read_worker_map_.end();
486 ++it) {
487 it->second->Cancel();
488 }
489 }
490
491 void DiskBasedCertCache::Get(const std::string& key, const GetCallback& cb) {
492 DCHECK(!key.empty());
493
494 ReadWorkerMap::iterator it = read_worker_map_.find(key);
495
496 if (it == read_worker_map_.end()) {
497 ReadWorker* worker =
498 new ReadWorker(backend_,
499 key,
500 base::Bind(&DiskBasedCertCache::FinishedReadOperation,
501 weak_factory_.GetWeakPtr(),
502 key));
503 read_worker_map_[key] = worker;
504 worker->AddCallback(cb);
505 worker->Start();
506 } else {
507 it->second->AddCallback(cb);
508 }
509 }
510
511 void DiskBasedCertCache::Set(const X509Certificate::OSCertHandle cert_handle,
512 const SetCallback& cb) {
513 DCHECK(!cb.is_null());
514 DCHECK(cert_handle);
515 std::string key = GetCacheKeyToCert(cert_handle);
516
517 WriteWorkerMap::iterator it = write_worker_map_.find(key);
518
519 if (it == write_worker_map_.end()) {
520 WriteWorker* worker =
521 new WriteWorker(backend_,
522 key,
523 cert_handle,
524 base::Bind(&DiskBasedCertCache::FinishedWriteOperation,
525 weak_factory_.GetWeakPtr(),
526 key));
527 write_worker_map_[key] = worker;
528 worker->AddCallback(cb);
529 worker->Start();
530 } else {
531 it->second->AddCallback(cb);
532 }
533 }
534
535 void DiskBasedCertCache::FinishedWriteOperation(const std::string& key) {
536 write_worker_map_.erase(key);
537 }
538
539 void DiskBasedCertCache::FinishedReadOperation(const std::string& key) {
540 read_worker_map_.erase(key);
541 }
542
543 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698