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

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: Workers now delete themselves, and DBCC deletion cancels workers. 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 {
19 std::string GetCacheKeyToCert(
wtc 2014/06/20 01:27:06 Nit: document this function.
20 const net::X509Certificate::OSCertHandle cert_handle) {
21 net::SHA1HashValue fingerprint =
22 net::X509Certificate::CalculateFingerprint(cert_handle);
wtc 2014/06/20 01:27:06 Nit: you can nest the unnamed namespace inside the
23
24 return "cert:" +
25 base::HexEncode(fingerprint.data, arraysize(fingerprint.data));
26 }
27 } // namespace
wtc 2014/06/20 01:27:06 Nit: I usually add a blank line after namespace
28
29 namespace net {
30
31 // WriteWorkers represent pending Set jobs in the DiskBasedCertCache. Each
32 // certificate requested to be cached is assigned a Writeworker on a one-to-one
33 // basis. The same certificate should not have multiple WriteWorkers at the same
34 // time; instead, add a user_callback_ to the existing WriteWorker.
35 class DiskBasedCertCache::WriteWorker {
36 public:
37 // |backend| is the backend to store |certificate| in, using
38 // |key| as the key for the disk_cache::Entry.
39 // |cleanup_callback| is called to clean up this ReadWorker,
40 // regardless of success or failure.
41 WriteWorker(disk_cache::Backend* backend,
42 const std::string& key,
43 const X509Certificate::OSCertHandle cert_handle,
44 const base::Closure& cleanup_callback);
45
46 ~WriteWorker();
47
48 // Writes the given certificate to the cache. On completion, will invoke all
49 // user callbacks.
50 void Start();
51
52 // Adds a callback to the set of callbacks to be notified when this
53 // WriteWorker finishes processing.
54 void AddCallback(const SetCallback& user_callback);
55
56 void Cancel();
57
58 private:
59 enum WriteState {
60 CREATE_OR_OPEN,
61 FINISH_CREATE_OR_OPEN,
62 START_WRITE,
63 FINISH_WRITE,
64 WRITE_NONE
wtc 2014/06/20 01:27:06 I suggest adding STATE_ prefix to these states.
65 };
66
67 void OnIOComplete(int rv);
68 int DoLoop(int rv);
69 int DoCreateOrOpen();
70 int DoFinishCreateOrOpen(int rv);
71 int DoStartWrite();
72 int DoFinishWrite(int rv);
73 void Finish(int rv);
74
75 // invokes all of the |user_callbacks_|
76 void CallCallbacks(int rv);
77
78 disk_cache::Backend* backend_;
79 const X509Certificate::OSCertHandle cert_handle_;
80 std::string key_;
81 bool canceled_;
82
83 disk_cache::Entry* entry_;
84 WriteState state_;
85 bool create_failed_;
86 scoped_refptr<IOBuffer> buffer;
87
88 base::Closure cleanup_callback_;
89 std::vector<SetCallback> user_callbacks_;
90 CompletionCallback io_callback_;
91
92 base::WeakPtrFactory<WriteWorker> weak_factory_;
93 };
wtc 2014/06/20 01:27:06 Please move the WriteWorker method definitions her
94
95 // ReadWorkers represent pending Get jobs in the DiskBasedCertCache. Each
96 // certificate requested to be retrieved from the cache is assigned a ReadWorker
97 // on a one-to-one basis. The same |key| should not have multiple ReadWorkers
98 // at the same time; instead, call AddCallback to add a user_callback_ to
99 // the the existing ReadWorker.
100 class DiskBasedCertCache::ReadWorker {
101 public:
102 // |backend| is the backend to read |certificate| from, using
103 // |key| as the key for the disk_cache::Entry.
104 // |cleanup_callback| is called to clean up this ReadWorker,
105 // regardless of success or failure.
106 ReadWorker(disk_cache::Backend* backend,
107 const std::string& key,
108 const base::Closure& cleanup_callback);
109
110 ~ReadWorker();
111
112 // Reads the given certificate from the cache. On completion, will invoke all
113 // user callbacks.
114 void Start();
115
116 // Adds a callback to the set of callbacks to be notified when this
117 // ReadWorker finishes processing.
118 void AddCallback(const GetCallback& user_callback);
119
120 void Cancel();
121
122 private:
123 enum ReadState { OPEN, START_READ, FINISH_READ, READ_NONE };
wtc 2014/06/20 01:27:06 Nit: I suggest adding STATE_ prefix to these state
124
125 void OnIOComplete(int rv);
126 int DoLoop(int rv);
127 int DoOpen();
128 int DoStartRead(int rv);
129 int DoFinishRead(int rv);
130 void Finish(int rv);
131
132 // invokes all of |user_callbacks_|
133 void CallCallbacks(int rv);
134
135 disk_cache::Backend* backend_;
136 X509Certificate::OSCertHandle cert_handle_;
137 std::string key_;
138 bool canceled_;
139
140 disk_cache::Entry* entry_;
141
142 ReadState state_;
143 int entry_size_;
144 scoped_refptr<IOBuffer> buffer;
145
146 base::Closure cleanup_callback_;
147 std::vector<GetCallback> user_callbacks_;
148 CompletionCallback io_callback_;
149 base::WeakPtrFactory<ReadWorker> weak_factory_;
150 };
151
152 DiskBasedCertCache::WriteWorker::WriteWorker(
153 disk_cache::Backend* backend,
154 const std::string& key,
155 X509Certificate::OSCertHandle cert_handle,
156 const base::Closure& cleanup_callback)
157 : backend_(backend),
158 cert_handle_(cert_handle),
159 key_(key),
160 canceled_(false),
161 entry_(NULL),
162 state_(CREATE_OR_OPEN),
163 create_failed_(false),
164 cleanup_callback_(cleanup_callback),
165 weak_factory_(this) {
166 io_callback_ =
167 base::Bind(&WriteWorker::OnIOComplete, weak_factory_.GetWeakPtr());
168 }
169
170 void DiskBasedCertCache::WriteWorker::Start() {
171 int rv = DoLoop(OK);
172
173 if (state_ != WRITE_NONE)
174 return;
175
176 Finish(rv);
177 }
178
179 void DiskBasedCertCache::WriteWorker::AddCallback(
180 const SetCallback& user_callback) {
181 user_callbacks_.push_back(user_callback);
182 }
183
184 void DiskBasedCertCache::WriteWorker::OnIOComplete(int rv) {
185 if (canceled_) {
186 Finish(ERR_FAILED);
187 return;
188 }
189
190 rv = DoLoop(rv);
191
192 if (state_ != WRITE_NONE)
193 return;
194
195 Finish(rv);
196 }
197
198 int DiskBasedCertCache::WriteWorker::DoLoop(int rv) {
199 do {
200 switch (state_) {
201 case CREATE_OR_OPEN:
202 rv = DoCreateOrOpen();
203 break;
204 case FINISH_CREATE_OR_OPEN:
205 rv = DoFinishCreateOrOpen(rv);
206 break;
207 case START_WRITE:
208 rv = DoStartWrite();
209 break;
210 case FINISH_WRITE:
211 rv = DoFinishWrite(rv);
212 break;
213 case WRITE_NONE:
214 break;
215 }
216 } while (rv != ERR_IO_PENDING && state_ != WRITE_NONE);
217
218 return rv;
219 }
220
221 int DiskBasedCertCache::WriteWorker::DoCreateOrOpen() {
222 state_ = FINISH_CREATE_OR_OPEN;
223
224 if (create_failed_)
225 return backend_->OpenEntry(key_, &entry_, io_callback_);
226
227 return backend_->CreateEntry(key_, &entry_, io_callback_);
228 }
229
230 int DiskBasedCertCache::WriteWorker::DoFinishCreateOrOpen(int rv) {
231 // ERR_FAILED implies create entry failed. In order to avoid trying
232 // to open the entry multiple times, the flag create_failed_ is set and
233 // checked.
234 if (rv == ERR_FAILED && !create_failed_) {
wtc 2014/06/20 01:27:06 We should test |rv < 0| instead of |rv == ERR_FAIL
235 create_failed_ = true;
236 state_ = CREATE_OR_OPEN;
237 return OK;
238 } else if (rv < 0) {
wtc 2014/06/20 01:27:06 Nit: In general, omit "else" if the previous block
239 state_ = WRITE_NONE;
240 return ERR_FAILED;
wtc 2014/06/20 01:27:07 IMPORTANT: in general we should report an accurate
241 }
242
243 state_ = START_WRITE;
244 return OK;
245 }
246
247 int DiskBasedCertCache::WriteWorker::DoStartWrite() {
248 std::string write_data;
249 bool encoded = X509Certificate::GetDEREncoded(cert_handle_, &write_data);
250
251 if (!encoded) {
252 state_ = WRITE_NONE;
253 return ERR_FAILED;
254 }
255
256 buffer = new IOBuffer(write_data.size());
257 memcpy(buffer->data(), write_data.data(), write_data.size());
258
259 state_ = FINISH_WRITE;
260
261 return entry_->WriteData(0 /* index */,
262 0 /* offset */,
263 buffer,
264 write_data.size(),
265 io_callback_,
266 true /* truncate */);
267 }
268
269 int DiskBasedCertCache::WriteWorker::DoFinishWrite(int rv) {
270 if (rv < 0) {
271 state_ = WRITE_NONE;
272 return ERR_FAILED;
273 }
274
275 state_ = WRITE_NONE;
276
277 return OK;
278 }
279
280 void DiskBasedCertCache::WriteWorker::CallCallbacks(int rv) {
281 for (std::vector<SetCallback>::iterator it = user_callbacks_.begin();
282 it != user_callbacks_.end();
283 it++) {
wtc 2014/06/20 01:27:06 Use ++it instead of it++ to increment an iterator,
284 base::ResetAndReturn(&(*it)).Run((rv >= 0) ? key_ : std::string());
wtc 2014/06/20 01:27:06 The use of base::ResetAndReturn is wrong. The pur
brandonsalmon 2014/06/20 18:07:23 Actually, is it even necessary to do this if it is
285 }
286 }
287
288 void DiskBasedCertCache::WriteWorker::Finish(int rv) {
289 if (entry_)
290 entry_->Close();
wtc 2014/06/20 01:27:06 This kind of cleanup is conventionally done in the
291 base::ResetAndReturn(&cleanup_callback_).Run();
292 CallCallbacks(rv);
293 delete this;
wtc 2014/06/20 01:27:06 IMPORTANT: the Finish() method must be written ver
294 }
295
296 void DiskBasedCertCache::WriteWorker::Cancel() {
297 canceled_ = true;
298 }
299
300 DiskBasedCertCache::WriteWorker::~WriteWorker() {
301 }
302
303 DiskBasedCertCache::ReadWorker::ReadWorker(
304 disk_cache::Backend* backend,
305 const std::string& key,
306 const base::Closure& cleanup_callback)
307 : backend_(backend),
308 cert_handle_(NULL),
309 key_(key),
310 canceled_(false),
311 entry_(NULL),
312 state_(OPEN),
313 entry_size_(0),
314 cleanup_callback_(cleanup_callback),
315 weak_factory_(this) {
316 io_callback_ =
317 base::Bind(&ReadWorker::OnIOComplete, weak_factory_.GetWeakPtr());
wtc 2014/06/20 01:27:06 Nit: you should be able to initialize io_callback_
brandonsalmon 2014/06/20 18:07:23 I'm going to leave this for now. I was talking wit
318 }
319
320 void DiskBasedCertCache::ReadWorker::Start() {
321 int rv = DoLoop(OK);
322
323 if (state_ != READ_NONE)
324 return;
325
326 Finish(rv);
327 }
328
329 void DiskBasedCertCache::ReadWorker::AddCallback(
330 const GetCallback& user_callback) {
331 user_callbacks_.push_back(user_callback);
332 }
333
334 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv) {
335 if (canceled_) {
336 Finish(ERR_FAILED);
337 return;
338 }
339
340 rv = DoLoop(rv);
341
342 if (state_ != READ_NONE)
343 return;
344
345 Finish(rv);
346 }
347
348 int DiskBasedCertCache::ReadWorker::DoLoop(int rv) {
349 do {
350 switch (state_) {
351 case OPEN:
352 rv = DoOpen();
353 break;
354 case START_READ:
355 rv = DoStartRead(rv);
356 break;
357 case FINISH_READ:
358 rv = DoFinishRead(rv);
359 break;
360 case READ_NONE:
361 break;
362 }
363 } while (rv != ERR_IO_PENDING && state_ != READ_NONE);
364
365 return rv;
366 }
367
368 int DiskBasedCertCache::ReadWorker::DoOpen() {
369 state_ = START_READ;
370 return backend_->OpenEntry(key_, &entry_, io_callback_);
371 }
372
373 int DiskBasedCertCache::ReadWorker::DoStartRead(int rv) {
374 if (rv < 0) {
375 state_ = READ_NONE;
376 return ERR_FAILED;
377 }
378
379 entry_size_ = entry_->GetDataSize(0 /* index */);
380 state_ = FINISH_READ;
381 buffer = new IOBuffer(entry_size_);
382 return entry_->ReadData(
383 0 /* index */, 0 /* offset */, buffer, entry_size_, io_callback_);
384 }
385
386 int DiskBasedCertCache::ReadWorker::DoFinishRead(int rv) {
387 if (rv < 0) {
388 state_ = READ_NONE;
389 return ERR_FAILED;
390 }
391
392 state_ = READ_NONE;
393
394 cert_handle_ =
395 X509Certificate::CreateOSCertHandleFromBytes(buffer->data(), entry_size_);
396
397 DCHECK(cert_handle_);
398 return OK;
399 }
400
401 void DiskBasedCertCache::ReadWorker::CallCallbacks(int rv) {
402 for (std::vector<GetCallback>::iterator it = user_callbacks_.begin();
403 it != user_callbacks_.end();
404 it++) {
405 base::ResetAndReturn(&(*it)).Run((rv >= 0) ? cert_handle_ : NULL);
406 }
407 }
408
409 void DiskBasedCertCache::ReadWorker::Finish(int rv) {
410 base::ResetAndReturn(&cleanup_callback_).Run();
411 CallCallbacks(rv);
412
413 if (entry_)
414 entry_->Close();
415 if (cert_handle_)
416 X509Certificate::FreeOSCertHandle(cert_handle_);
417 delete this;
418 }
419
420 void DiskBasedCertCache::ReadWorker::Cancel() {
421 canceled_ = true;
422 }
423
424 DiskBasedCertCache::ReadWorker::~ReadWorker() {
425 }
426
427 DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend* backend)
428 : backend_(backend), weak_factory_(this) {
429 DCHECK(backend_);
430 }
431
432 DiskBasedCertCache::~DiskBasedCertCache() {
433 for (WriteWorkerMap::iterator it = write_worker_map_.begin();
434 it != write_worker_map_.end();
435 it++)
436 it->second->Cancel();
437 for (ReadWorkerMap::iterator it = read_worker_map_.begin();
438 it != read_worker_map_.end();
439 it++)
440 it->second->Cancel();
441 }
442
443 void DiskBasedCertCache::Get(const std::string& key, const GetCallback& cb) {
444 DCHECK(!key.empty());
445
446 ReadWorkerMap::iterator it = read_worker_map_.find(key);
447
448 if (it == read_worker_map_.end()) {
449 std::pair<ReadWorkerMap::iterator, bool> entry = read_worker_map_.insert(
450 make_pair(key,
451 new ReadWorker(
452 backend_,
453 key,
454 base::Bind(&DiskBasedCertCache::FinishedReadOperation,
455 weak_factory_.GetWeakPtr(),
456 key))));
457 DCHECK(entry.second);
458 entry.first->second->AddCallback(cb);
459 entry.first->second->Start();
460 } else {
461 it->second->AddCallback(cb);
462 }
463 }
464
465 void DiskBasedCertCache::Set(const X509Certificate::OSCertHandle cert_handle,
466 const SetCallback& cb) {
467 DCHECK(!cb.is_null());
468 DCHECK(cert_handle);
469 std::string key = GetCacheKeyToCert(cert_handle);
470
471 WriteWorkerMap::iterator it = write_worker_map_.find(key);
472
473 if (it == write_worker_map_.end()) {
474 std::pair<WriteWorkerMap::iterator, bool> entry = write_worker_map_.insert(
wtc 2014/06/20 01:27:06 Here, you can just use the associative-array notat
475 make_pair(key,
476 new WriteWorker(
477 backend_,
478 key,
479 cert_handle,
480 base::Bind(&DiskBasedCertCache::FinishedWriteOperation,
481 weak_factory_.GetWeakPtr(),
482 key))));
483 DCHECK(entry.second);
484 entry.first->second->AddCallback(cb);
485 entry.first->second->Start();
486 } else {
487 it->second->AddCallback(cb);
488 }
489 }
490
491 void DiskBasedCertCache::FinishedWriteOperation(const std::string& key) {
492 write_worker_map_.erase(key);
493 }
494
495 void DiskBasedCertCache::FinishedReadOperation(const std::string& key) {
496 read_worker_map_.erase(key);
497 }
498
499 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698