OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/http/disk_cache_based_quic_server_info.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/callback.h" | |
9 #include "base/callback_helpers.h" | |
10 #include "base/logging.h" | |
11 #include "base/metrics/histogram.h" | |
12 #include "base/profiler/scoped_tracker.h" | |
13 #include "net/base/completion_callback.h" | |
14 #include "net/base/io_buffer.h" | |
15 #include "net/base/net_errors.h" | |
16 #include "net/http/http_cache.h" | |
17 #include "net/http/http_network_session.h" | |
18 #include "net/quic/quic_server_id.h" | |
19 | |
20 namespace net { | |
21 | |
22 // Some APIs inside disk_cache take a handle that the caller must keep alive | |
23 // until the API has finished its asynchronous execution. | |
24 // | |
25 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the | |
26 // operation completes causing a use-after-free. | |
27 // | |
28 // This data shim struct is meant to provide a location for the disk_cache | |
29 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo | |
30 // object has been deleted. The lifetime for instances of this struct | |
31 // should be bound to the CompletionCallback that is passed to the disk_cache | |
32 // API. We do this by binding an instance of this struct to an unused | |
33 // parameter for OnIOComplete() using base::Owned(). | |
34 // | |
35 // This is a hack. A better fix is to make it so that the disk_cache APIs | |
36 // take a Callback to a mutator for setting the output value rather than | |
37 // writing into a raw handle. Then the caller can just pass in a Callback | |
38 // bound to WeakPtr for itself. This callback would correctly "no-op" itself | |
39 // when the DiskCacheBasedQuicServerInfo object is deleted. | |
40 // | |
41 // TODO(ajwong): Change disk_cache's API to return results via Callback. | |
42 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim { | |
43 CacheOperationDataShim() : backend(NULL), entry(NULL) {} | |
44 | |
45 disk_cache::Backend* backend; | |
46 disk_cache::Entry* entry; | |
47 }; | |
48 | |
49 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo( | |
50 const QuicServerId& server_id, | |
51 HttpCache* http_cache) | |
52 : QuicServerInfo(server_id), | |
53 data_shim_(new CacheOperationDataShim()), | |
54 state_(GET_BACKEND), | |
55 ready_(false), | |
56 found_entry_(false), | |
57 server_id_(server_id), | |
58 http_cache_(http_cache), | |
59 backend_(NULL), | |
60 entry_(NULL), | |
61 last_failure_(NO_FAILURE), | |
62 weak_factory_(this) { | |
63 io_callback_ = | |
64 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete, | |
65 weak_factory_.GetWeakPtr(), | |
66 base::Owned(data_shim_)); // Ownership assigned. | |
67 } | |
68 | |
69 void DiskCacheBasedQuicServerInfo::Start() { | |
70 DCHECK(CalledOnValidThread()); | |
71 DCHECK_EQ(GET_BACKEND, state_); | |
72 DCHECK_EQ(last_failure_, NO_FAILURE); | |
73 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START); | |
74 load_start_time_ = base::TimeTicks::Now(); | |
75 DoLoop(OK); | |
76 } | |
77 | |
78 int DiskCacheBasedQuicServerInfo::WaitForDataReady( | |
79 const CompletionCallback& callback) { | |
80 DCHECK(CalledOnValidThread()); | |
81 DCHECK_NE(GET_BACKEND, state_); | |
82 wait_for_data_start_time_ = base::TimeTicks::Now(); | |
83 | |
84 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY); | |
85 if (ready_) { | |
86 wait_for_data_end_time_ = base::TimeTicks::Now(); | |
87 RecordLastFailure(); | |
88 return OK; | |
89 } | |
90 | |
91 if (!callback.is_null()) { | |
92 // Prevent a new callback for WaitForDataReady overwriting an existing | |
93 // pending callback (|wait_for_ready_callback_|). | |
94 if (!wait_for_ready_callback_.is_null()) { | |
95 RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE); | |
96 return ERR_INVALID_ARGUMENT; | |
97 } | |
98 wait_for_ready_callback_ = callback; | |
99 } | |
100 | |
101 return ERR_IO_PENDING; | |
102 } | |
103 | |
104 void DiskCacheBasedQuicServerInfo::ResetWaitForDataReadyCallback() { | |
105 DCHECK(CalledOnValidThread()); | |
106 wait_for_ready_callback_.Reset(); | |
107 } | |
108 | |
109 void DiskCacheBasedQuicServerInfo::CancelWaitForDataReadyCallback() { | |
110 DCHECK(CalledOnValidThread()); | |
111 | |
112 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL); | |
113 if (!wait_for_ready_callback_.is_null()) { | |
114 RecordLastFailure(); | |
115 wait_for_ready_callback_.Reset(); | |
116 } | |
117 } | |
118 | |
119 bool DiskCacheBasedQuicServerInfo::IsDataReady() { | |
120 return ready_; | |
121 } | |
122 | |
123 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() { | |
124 // The data can be persisted if it has been loaded from the disk cache | |
125 // and there are no pending writes. | |
126 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST); | |
127 if (ready_ && new_data_.empty()) | |
128 return true; | |
129 RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE); | |
130 return false; | |
131 } | |
132 | |
133 void DiskCacheBasedQuicServerInfo::Persist() { | |
134 DCHECK(CalledOnValidThread()); | |
135 if (!IsReadyToPersist()) { | |
136 // Handle updates while a write is pending or if we haven't loaded from disk | |
137 // cache. Save the data to be written into a temporary buffer and then | |
138 // persist that data when we are ready to persist. | |
139 pending_write_data_ = Serialize(); | |
140 return; | |
141 } | |
142 PersistInternal(); | |
143 } | |
144 | |
145 void DiskCacheBasedQuicServerInfo::PersistInternal() { | |
146 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
147 tracked_objects::ScopedTracker tracking_profile( | |
148 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
149 "422516 DiskCacheBasedQuicServerInfo::PersistInternal")); | |
150 | |
151 DCHECK(CalledOnValidThread()); | |
152 DCHECK_NE(GET_BACKEND, state_); | |
153 DCHECK(new_data_.empty()); | |
154 CHECK(ready_); | |
155 DCHECK(wait_for_ready_callback_.is_null()); | |
156 | |
157 if (pending_write_data_.empty()) { | |
158 new_data_ = Serialize(); | |
159 } else { | |
160 new_data_ = pending_write_data_; | |
161 pending_write_data_.clear(); | |
162 } | |
163 | |
164 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST); | |
165 if (!backend_) { | |
166 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE); | |
167 return; | |
168 } | |
169 | |
170 state_ = CREATE_OR_OPEN; | |
171 DoLoop(OK); | |
172 } | |
173 | |
174 void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() { | |
175 DCHECK(CalledOnValidThread()); | |
176 DCHECK_NE(GET_BACKEND, state_); | |
177 | |
178 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT); | |
179 if (!backend_) { | |
180 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE); | |
181 return; | |
182 } | |
183 | |
184 backend_->OnExternalCacheHit(key()); | |
185 } | |
186 | |
187 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() { | |
188 DCHECK(wait_for_ready_callback_.is_null()); | |
189 if (entry_) | |
190 entry_->Close(); | |
191 } | |
192 | |
193 std::string DiskCacheBasedQuicServerInfo::key() const { | |
194 return "quicserverinfo:" + server_id_.ToString(); | |
195 } | |
196 | |
197 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused, | |
198 int rv) { | |
199 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
200 tracked_objects::ScopedTracker tracking_profile( | |
201 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
202 "422516 DiskCacheBasedQuicServerInfo::OnIOComplete")); | |
203 | |
204 DCHECK_NE(NONE, state_); | |
205 rv = DoLoop(rv); | |
206 if (rv == ERR_IO_PENDING) | |
207 return; | |
208 | |
209 base::WeakPtr<DiskCacheBasedQuicServerInfo> weak_this = | |
210 weak_factory_.GetWeakPtr(); | |
211 | |
212 if (!wait_for_ready_callback_.is_null()) { | |
213 wait_for_data_end_time_ = base::TimeTicks::Now(); | |
214 RecordLastFailure(); | |
215 base::ResetAndReturn(&wait_for_ready_callback_).Run(rv); | |
216 } | |
217 // |wait_for_ready_callback_| could delete the object if there is an error. | |
218 // Check if |weak_this| still exists before accessing it. | |
219 if (weak_this.get() && ready_ && !pending_write_data_.empty()) { | |
220 DCHECK_EQ(NONE, state_); | |
221 PersistInternal(); | |
222 } | |
223 } | |
224 | |
225 int DiskCacheBasedQuicServerInfo::DoLoop(int rv) { | |
226 do { | |
227 switch (state_) { | |
228 case GET_BACKEND: | |
229 rv = DoGetBackend(); | |
230 break; | |
231 case GET_BACKEND_COMPLETE: | |
232 rv = DoGetBackendComplete(rv); | |
233 break; | |
234 case OPEN: | |
235 rv = DoOpen(); | |
236 break; | |
237 case OPEN_COMPLETE: | |
238 rv = DoOpenComplete(rv); | |
239 break; | |
240 case READ: | |
241 rv = DoRead(); | |
242 break; | |
243 case READ_COMPLETE: | |
244 rv = DoReadComplete(rv); | |
245 break; | |
246 case WAIT_FOR_DATA_READY_DONE: | |
247 rv = DoWaitForDataReadyDone(); | |
248 break; | |
249 case CREATE_OR_OPEN: | |
250 rv = DoCreateOrOpen(); | |
251 break; | |
252 case CREATE_OR_OPEN_COMPLETE: | |
253 rv = DoCreateOrOpenComplete(rv); | |
254 break; | |
255 case WRITE: | |
256 rv = DoWrite(); | |
257 break; | |
258 case WRITE_COMPLETE: | |
259 rv = DoWriteComplete(rv); | |
260 break; | |
261 case SET_DONE: | |
262 rv = DoSetDone(); | |
263 break; | |
264 default: | |
265 rv = OK; | |
266 NOTREACHED(); | |
267 } | |
268 } while (rv != ERR_IO_PENDING && state_ != NONE); | |
269 | |
270 return rv; | |
271 } | |
272 | |
273 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) { | |
274 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
275 tracked_objects::ScopedTracker tracking_profile( | |
276 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
277 "422516 DiskCacheBasedQuicServerInfo::DoGetBackendComplete")); | |
278 | |
279 if (rv == OK) { | |
280 backend_ = data_shim_->backend; | |
281 state_ = OPEN; | |
282 } else { | |
283 RecordQuicServerInfoFailure(GET_BACKEND_FAILURE); | |
284 state_ = WAIT_FOR_DATA_READY_DONE; | |
285 } | |
286 return OK; | |
287 } | |
288 | |
289 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) { | |
290 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
291 tracked_objects::ScopedTracker tracking_profile( | |
292 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
293 "422516 DiskCacheBasedQuicServerInfo::DoOpenComplete")); | |
294 | |
295 if (rv == OK) { | |
296 entry_ = data_shim_->entry; | |
297 state_ = READ; | |
298 found_entry_ = true; | |
299 } else { | |
300 RecordQuicServerInfoFailure(OPEN_FAILURE); | |
301 state_ = WAIT_FOR_DATA_READY_DONE; | |
302 } | |
303 | |
304 return OK; | |
305 } | |
306 | |
307 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) { | |
308 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
309 tracked_objects::ScopedTracker tracking_profile( | |
310 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
311 "422516 DiskCacheBasedQuicServerInfo::DoReadComplete")); | |
312 | |
313 if (rv > 0) | |
314 data_.assign(read_buffer_->data(), rv); | |
315 else if (rv < 0) | |
316 RecordQuicServerInfoFailure(READ_FAILURE); | |
317 | |
318 state_ = WAIT_FOR_DATA_READY_DONE; | |
319 return OK; | |
320 } | |
321 | |
322 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) { | |
323 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
324 tracked_objects::ScopedTracker tracking_profile( | |
325 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
326 "422516 DiskCacheBasedQuicServerInfo::DoWriteComplete")); | |
327 | |
328 if (rv < 0) | |
329 RecordQuicServerInfoFailure(WRITE_FAILURE); | |
330 state_ = SET_DONE; | |
331 return OK; | |
332 } | |
333 | |
334 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) { | |
335 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
336 tracked_objects::ScopedTracker tracking_profile( | |
337 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
338 "422516 DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete")); | |
339 | |
340 if (rv != OK) { | |
341 RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE); | |
342 state_ = SET_DONE; | |
343 } else { | |
344 if (!entry_) { | |
345 entry_ = data_shim_->entry; | |
346 found_entry_ = true; | |
347 } | |
348 DCHECK(entry_); | |
349 state_ = WRITE; | |
350 } | |
351 return OK; | |
352 } | |
353 | |
354 int DiskCacheBasedQuicServerInfo::DoGetBackend() { | |
355 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
356 tracked_objects::ScopedTracker tracking_profile( | |
357 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
358 "422516 DiskCacheBasedQuicServerInfo::DoGetBackend")); | |
359 | |
360 state_ = GET_BACKEND_COMPLETE; | |
361 return http_cache_->GetBackend(&data_shim_->backend, io_callback_); | |
362 } | |
363 | |
364 int DiskCacheBasedQuicServerInfo::DoOpen() { | |
365 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
366 tracked_objects::ScopedTracker tracking_profile( | |
367 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
368 "422516 DiskCacheBasedQuicServerInfo::DoOpen")); | |
369 | |
370 state_ = OPEN_COMPLETE; | |
371 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); | |
372 } | |
373 | |
374 int DiskCacheBasedQuicServerInfo::DoRead() { | |
375 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
376 tracked_objects::ScopedTracker tracking_profile( | |
377 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
378 "422516 DiskCacheBasedQuicServerInfo::DoRead")); | |
379 | |
380 const int32 size = entry_->GetDataSize(0 /* index */); | |
381 if (!size) { | |
382 state_ = WAIT_FOR_DATA_READY_DONE; | |
383 return OK; | |
384 } | |
385 | |
386 read_buffer_ = new IOBuffer(size); | |
387 state_ = READ_COMPLETE; | |
388 return entry_->ReadData( | |
389 0 /* index */, 0 /* offset */, read_buffer_.get(), size, io_callback_); | |
390 } | |
391 | |
392 int DiskCacheBasedQuicServerInfo::DoWrite() { | |
393 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
394 tracked_objects::ScopedTracker tracking_profile( | |
395 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
396 "422516 DiskCacheBasedQuicServerInfo::DoWrite")); | |
397 | |
398 write_buffer_ = new IOBuffer(new_data_.size()); | |
399 memcpy(write_buffer_->data(), new_data_.data(), new_data_.size()); | |
400 state_ = WRITE_COMPLETE; | |
401 | |
402 return entry_->WriteData(0 /* index */, | |
403 0 /* offset */, | |
404 write_buffer_.get(), | |
405 new_data_.size(), | |
406 io_callback_, | |
407 true /* truncate */); | |
408 } | |
409 | |
410 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() { | |
411 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
412 tracked_objects::ScopedTracker tracking_profile( | |
413 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
414 "422516 DiskCacheBasedQuicServerInfo::DoCreateOrOpen")); | |
415 | |
416 state_ = CREATE_OR_OPEN_COMPLETE; | |
417 if (entry_) | |
418 return OK; | |
419 | |
420 if (found_entry_) { | |
421 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); | |
422 } | |
423 | |
424 return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_); | |
425 } | |
426 | |
427 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() { | |
428 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
429 tracked_objects::ScopedTracker tracking_profile( | |
430 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
431 "422516 DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone")); | |
432 | |
433 DCHECK(!ready_); | |
434 state_ = NONE; | |
435 ready_ = true; | |
436 // We close the entry because, if we shutdown before ::Persist is called, | |
437 // then we might leak a cache reference, which causes a DCHECK on shutdown. | |
438 if (entry_) | |
439 entry_->Close(); | |
440 entry_ = NULL; | |
441 | |
442 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE); | |
443 if (!Parse(data_)) { | |
444 if (data_.empty()) | |
445 RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE); | |
446 else | |
447 RecordQuicServerInfoFailure(PARSE_FAILURE); | |
448 } | |
449 | |
450 UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime", | |
451 base::TimeTicks::Now() - load_start_time_); | |
452 return OK; | |
453 } | |
454 | |
455 int DiskCacheBasedQuicServerInfo::DoSetDone() { | |
456 if (entry_) | |
457 entry_->Close(); | |
458 entry_ = NULL; | |
459 new_data_.clear(); | |
460 state_ = NONE; | |
461 return OK; | |
462 } | |
463 | |
464 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus( | |
465 QuicServerInfoAPICall call) { | |
466 if (!backend_) { | |
467 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call, | |
468 QUIC_SERVER_INFO_NUM_OF_API_CALLS); | |
469 } else if (backend_->GetCacheType() == net::MEMORY_CACHE) { | |
470 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call, | |
471 QUIC_SERVER_INFO_NUM_OF_API_CALLS); | |
472 } else { | |
473 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call, | |
474 QUIC_SERVER_INFO_NUM_OF_API_CALLS); | |
475 } | |
476 } | |
477 | |
478 void DiskCacheBasedQuicServerInfo::RecordLastFailure() { | |
479 if (last_failure_ != NO_FAILURE) { | |
480 UMA_HISTOGRAM_ENUMERATION( | |
481 "Net.QuicDiskCache.FailureReason.WaitForDataReady", | |
482 last_failure_, NUM_OF_FAILURES); | |
483 } | |
484 last_failure_ = NO_FAILURE; | |
485 } | |
486 | |
487 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure( | |
488 FailureReason failure) { | |
489 last_failure_ = failure; | |
490 | |
491 if (!backend_) { | |
492 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend", | |
493 failure, NUM_OF_FAILURES); | |
494 } else if (backend_->GetCacheType() == net::MEMORY_CACHE) { | |
495 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache", | |
496 failure, NUM_OF_FAILURES); | |
497 } else { | |
498 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache", | |
499 failure, NUM_OF_FAILURES); | |
500 } | |
501 } | |
502 | |
503 } // namespace net | |
OLD | NEW |