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

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: Fixed memory leak by remembering to close entry and free OSCertHandle. 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
181 return rv;
182 }
183
184 int DiskBasedCertCache::WriteWorker::DoCreate() {
185 state_ = STATE_CREATE_COMPLETE;
186
187 return backend_->CreateEntry(key_, &entry_, io_callback_);
188 }
189
190 int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv) {
191 // An error here usually signifies that the entry already exists.
192 // If this occurs, it is necessary to instead open the previously
193 // existing entry.
194 if (rv < 0) {
195 state_ = STATE_OPEN;
196 return OK;
197 }
198
199 state_ = STATE_WRITE;
200 return OK;
201 }
202
203 int DiskBasedCertCache::WriteWorker::DoOpen() {
204 state_ = STATE_OPEN_COMPLETE;
205 return backend_->OpenEntry(key_, &entry_, io_callback_);
206 }
207
208 int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv) {
209 if (rv < 0) {
210 state_ = STATE_NONE;
211 return rv;
212 }
213 state_ = STATE_WRITE;
214 return OK;
215 }
216
217 int DiskBasedCertCache::WriteWorker::DoWrite() {
218 std::string write_data;
219 bool encoded = X509Certificate::GetDEREncoded(cert_handle_, &write_data);
220
221 if (!encoded) {
222 state_ = STATE_NONE;
223 return ERR_FAILED;
224 }
225
226 buffer_ = new IOBuffer(write_data.size());
227 io_buf_len_ = write_data.size();
228 memcpy(buffer_->data(), write_data.data(), io_buf_len_);
229
230 state_ = STATE_WRITE_COMPLETE;
231
232 return entry_->WriteData(0 /* index */,
233 0 /* offset */,
234 buffer_,
235 write_data.size(),
236 io_callback_,
237 true /* truncate */);
238 }
239
240 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv) {
241 state_ = STATE_NONE;
242 if (rv < io_buf_len_)
243 return ERR_FAILED;
244
245 return OK;
246 }
247
248 void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv) {
249 std::string key;
250 if (rv >= 0)
251 key = key_;
252
253 for (std::vector<SetCallback>::const_iterator it = user_callbacks_.begin();
254 it != user_callbacks_.end();
255 ++it) {
256 it->Run(key);
257 }
258 user_callbacks_.clear();
259 }
260
261 void DiskBasedCertCache::WriteWorker::Finish(int rv) {
262 cleanup_callback_.Run();
263 cleanup_callback_.Reset();
264 RunCallbacks(rv);
265 delete this;
266 }
267
268 void DiskBasedCertCache::WriteWorker::Cancel() {
269 canceled_ = true;
270 }
271
272 DiskBasedCertCache::WriteWorker::~WriteWorker() {
273 if (entry_)
274 entry_->Close();
275 }
276
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 base::Closure& cleanup_callback);
291
292 ~ReadWorker();
293
294 // Reads the given certificate from the cache. On completion, will invoke all
295 // user callbacks.
296 void Start();
297
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);
301
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();
306
307 private:
308 enum State {
309 STATE_OPEN,
310 STATE_OPEN_COMPLETE,
311 STATE_READ,
312 STATE_READ_COMPLETE,
313 STATE_NONE
314 };
315
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);
323
324 // Invokes all of |user_callbacks_|
325 void RunCallbacks();
326
327 disk_cache::Backend* backend_;
328 X509Certificate::OSCertHandle cert_handle_;
329 std::string key_;
330 bool canceled_;
331
332 disk_cache::Entry* entry_;
333
334 State state_;
335 scoped_refptr<IOBuffer> buffer_;
336 int io_buf_len_;
337
338 base::Closure cleanup_callback_;
339 std::vector<GetCallback> user_callbacks_;
340 CompletionCallback io_callback_;
341 };
342
343 DiskBasedCertCache::ReadWorker::ReadWorker(
344 disk_cache::Backend* backend,
345 const std::string& key,
346 const base::Closure& cleanup_callback)
347 : backend_(backend),
348 cert_handle_(NULL),
349 key_(key),
350 canceled_(false),
351 entry_(NULL),
352 state_(STATE_NONE),
353 io_buf_len_(0),
354 cleanup_callback_(cleanup_callback),
355 io_callback_(
356 base::Bind(&ReadWorker::OnIOComplete, base::Unretained(this))) {
357 }
358
359 void DiskBasedCertCache::ReadWorker::Start() {
360 DCHECK_EQ(STATE_NONE, state_);
361 state_ = STATE_OPEN;
362 int rv = DoLoop(OK);
363
364 if (rv == ERR_IO_PENDING)
365 return;
366
367 Finish(rv);
368 }
369
370 void DiskBasedCertCache::ReadWorker::AddCallback(
371 const GetCallback& user_callback) {
372 user_callbacks_.push_back(user_callback);
373 }
374
375 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv) {
376 if (canceled_) {
377 Finish(ERR_FAILED);
378 return;
379 }
380
381 rv = DoLoop(rv);
382
383 if (rv == ERR_IO_PENDING)
384 return;
385
386 Finish(rv);
387 }
388
389 int DiskBasedCertCache::ReadWorker::DoLoop(int rv) {
390 do {
391 State next_state = state_;
392 state_ = STATE_NONE;
393 switch (next_state) {
394 case STATE_OPEN:
395 rv = DoOpen();
396 break;
397 case STATE_OPEN_COMPLETE:
398 rv = DoOpenComplete(rv);
399 break;
400 case STATE_READ:
401 rv = DoRead();
402 break;
403 case STATE_READ_COMPLETE:
404 rv = DoReadComplete(rv);
405 break;
406 case STATE_NONE:
407 NOTREACHED();
408 break;
409 }
410 } while (rv != ERR_IO_PENDING && state_ != STATE_NONE);
411
412 return rv;
413 }
414
415 int DiskBasedCertCache::ReadWorker::DoOpen() {
416 state_ = STATE_OPEN_COMPLETE;
417 return backend_->OpenEntry(key_, &entry_, io_callback_);
418 }
419
420 int DiskBasedCertCache::ReadWorker::DoOpenComplete(int rv) {
421 if (rv < 0) {
422 state_ = STATE_NONE;
423 return rv;
424 }
425 state_ = STATE_READ;
426 return OK;
427 }
428
429 int DiskBasedCertCache::ReadWorker::DoRead() {
430 state_ = STATE_READ_COMPLETE;
431 io_buf_len_ = entry_->GetDataSize(0 /* index */);
432 buffer_ = new IOBuffer(io_buf_len_);
433 return entry_->ReadData(
434 0 /* index */, 0 /* offset */, buffer_, io_buf_len_, io_callback_);
435 }
436
437 int DiskBasedCertCache::ReadWorker::DoReadComplete(int rv) {
438 state_ = STATE_NONE;
439 if (rv < io_buf_len_)
440 return ERR_FAILED;
441
442 cert_handle_ = X509Certificate::CreateOSCertHandleFromBytes(buffer_->data(),
443 io_buf_len_);
444 if (!cert_handle_)
445 return ERR_FAILED;
446
447 return OK;
448 }
449
450 void DiskBasedCertCache::ReadWorker::RunCallbacks() {
451 for (std::vector<GetCallback>::const_iterator it = user_callbacks_.begin();
452 it != user_callbacks_.end();
453 ++it) {
454 it->Run(cert_handle_);
455 }
456 user_callbacks_.clear();
457 }
458
459 void DiskBasedCertCache::ReadWorker::Finish(int rv) {
460 cleanup_callback_.Run();
461 cleanup_callback_.Reset();
462 RunCallbacks();
463 delete this;
464 }
465
466 void DiskBasedCertCache::ReadWorker::Cancel() {
467 canceled_ = true;
468 }
469
470 DiskBasedCertCache::ReadWorker::~ReadWorker() {
471 if (entry_)
472 entry_->Close();
473 if (cert_handle_)
474 X509Certificate::FreeOSCertHandle(cert_handle_);
475 }
476
477 DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend* backend)
478 : backend_(backend), weak_factory_(this) {
479 DCHECK(backend_);
480 }
481
482 DiskBasedCertCache::~DiskBasedCertCache() {
483 for (WriteWorkerMap::iterator it = write_worker_map_.begin();
484 it != write_worker_map_.end();
485 ++it) {
486 it->second->Cancel();
487 }
488 for (ReadWorkerMap::iterator it = read_worker_map_.begin();
489 it != read_worker_map_.end();
490 ++it) {
491 it->second->Cancel();
492 }
493 }
494
495 void DiskBasedCertCache::Get(const std::string& key, const GetCallback& cb) {
496 DCHECK(!key.empty());
497
498 ReadWorkerMap::iterator it = read_worker_map_.find(key);
499
500 if (it == read_worker_map_.end()) {
501 ReadWorker* worker =
502 new ReadWorker(backend_,
503 key,
504 base::Bind(&DiskBasedCertCache::FinishedReadOperation,
505 weak_factory_.GetWeakPtr(),
506 key));
507 read_worker_map_[key] = worker;
508 worker->AddCallback(cb);
509 worker->Start();
510 } else {
511 it->second->AddCallback(cb);
512 }
513 }
514
515 void DiskBasedCertCache::Set(const X509Certificate::OSCertHandle cert_handle,
516 const SetCallback& cb) {
517 DCHECK(!cb.is_null());
518 DCHECK(cert_handle);
519 std::string key = GetCacheKeyToCert(cert_handle);
520
521 WriteWorkerMap::iterator it = write_worker_map_.find(key);
522
523 if (it == write_worker_map_.end()) {
524 WriteWorker* worker =
525 new WriteWorker(backend_,
526 key,
527 cert_handle,
528 base::Bind(&DiskBasedCertCache::FinishedWriteOperation,
529 weak_factory_.GetWeakPtr(),
530 key));
531 write_worker_map_[key] = worker;
532 worker->AddCallback(cb);
533 worker->Start();
534 } else {
535 it->second->AddCallback(cb);
536 }
537 }
538
539 void DiskBasedCertCache::FinishedWriteOperation(const std::string& key) {
540 write_worker_map_.erase(key);
541 }
542
543 void DiskBasedCertCache::FinishedReadOperation(const std::string& key) {
544 read_worker_map_.erase(key);
545 }
546
547 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698