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_macros.h" | |
12 #include "base/stl_util.h" | |
13 #include "base/trace_event/memory_usage_estimator.h" | |
14 #include "net/base/completion_callback.h" | |
15 #include "net/base/io_buffer.h" | |
16 #include "net/base/net_errors.h" | |
17 #include "net/http/http_cache.h" | |
18 #include "net/http/http_network_session.h" | |
19 #include "net/quic/core/quic_server_id.h" | |
20 | |
21 namespace net { | |
22 | |
23 // Some APIs inside disk_cache take a handle that the caller must keep alive | |
24 // until the API has finished its asynchronous execution. | |
25 // | |
26 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the | |
27 // operation completes causing a use-after-free. | |
28 // | |
29 // This data shim struct is meant to provide a location for the disk_cache | |
30 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo | |
31 // object has been deleted. The lifetime for instances of this struct | |
32 // should be bound to the CompletionCallback that is passed to the disk_cache | |
33 // API. We do this by binding an instance of this struct to an unused | |
34 // parameter for OnIOComplete() using base::Owned(). | |
35 // | |
36 // This is a hack. A better fix is to make it so that the disk_cache APIs | |
37 // take a Callback to a mutator for setting the output value rather than | |
38 // writing into a raw handle. Then the caller can just pass in a Callback | |
39 // bound to WeakPtr for itself. This callback would correctly "no-op" itself | |
40 // when the DiskCacheBasedQuicServerInfo object is deleted. | |
41 // | |
42 // TODO(ajwong): Change disk_cache's API to return results via Callback. | |
43 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim { | |
44 CacheOperationDataShim() : backend(NULL), entry(NULL) {} | |
45 | |
46 disk_cache::Backend* backend; | |
47 disk_cache::Entry* entry; | |
48 }; | |
49 | |
50 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo( | |
51 const QuicServerId& server_id, | |
52 HttpCache* http_cache) | |
53 : QuicServerInfo(server_id), | |
54 data_shim_(new CacheOperationDataShim()), | |
55 state_(GET_BACKEND), | |
56 ready_(false), | |
57 found_entry_(false), | |
58 server_id_(server_id), | |
59 http_cache_(http_cache), | |
60 backend_(NULL), | |
61 entry_(NULL), | |
62 last_failure_(NO_FAILURE), | |
63 weak_factory_(this) { | |
64 io_callback_ = | |
65 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete, | |
66 weak_factory_.GetWeakPtr(), | |
67 base::Owned(data_shim_)); // Ownership assigned. | |
68 } | |
69 | |
70 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() { | |
71 DCHECK(wait_for_ready_callback_.is_null()); | |
72 if (entry_) | |
73 entry_->Close(); | |
74 } | |
75 | |
76 void DiskCacheBasedQuicServerInfo::Start() { | |
77 DCHECK(CalledOnValidThread()); | |
78 DCHECK_EQ(GET_BACKEND, state_); | |
79 DCHECK_EQ(last_failure_, NO_FAILURE); | |
80 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START); | |
81 load_start_time_ = base::TimeTicks::Now(); | |
82 DoLoop(OK); | |
83 } | |
84 | |
85 int DiskCacheBasedQuicServerInfo::WaitForDataReady( | |
86 const CompletionCallback& callback) { | |
87 DCHECK(CalledOnValidThread()); | |
88 DCHECK_NE(GET_BACKEND, state_); | |
89 wait_for_data_start_time_ = base::TimeTicks::Now(); | |
90 | |
91 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY); | |
92 if (ready_) { | |
93 wait_for_data_end_time_ = base::TimeTicks::Now(); | |
94 RecordLastFailure(); | |
95 return OK; | |
96 } | |
97 | |
98 if (!callback.is_null()) { | |
99 // Prevent a new callback for WaitForDataReady overwriting an existing | |
100 // pending callback (|wait_for_ready_callback_|). | |
101 if (!wait_for_ready_callback_.is_null()) { | |
102 RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE); | |
103 return ERR_INVALID_ARGUMENT; | |
104 } | |
105 wait_for_ready_callback_ = callback; | |
106 } | |
107 | |
108 return ERR_IO_PENDING; | |
109 } | |
110 | |
111 void DiskCacheBasedQuicServerInfo::ResetWaitForDataReadyCallback() { | |
112 DCHECK(CalledOnValidThread()); | |
113 wait_for_ready_callback_.Reset(); | |
114 } | |
115 | |
116 void DiskCacheBasedQuicServerInfo::CancelWaitForDataReadyCallback() { | |
117 DCHECK(CalledOnValidThread()); | |
118 | |
119 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL); | |
120 if (!wait_for_ready_callback_.is_null()) { | |
121 RecordLastFailure(); | |
122 wait_for_ready_callback_.Reset(); | |
123 } | |
124 } | |
125 | |
126 bool DiskCacheBasedQuicServerInfo::IsDataReady() { | |
127 return ready_; | |
128 } | |
129 | |
130 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() { | |
131 // The data can be persisted if it has been loaded from the disk cache | |
132 // and there are no pending writes. | |
133 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST); | |
134 if (ready_ && new_data_.empty()) | |
135 return true; | |
136 RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE); | |
137 return false; | |
138 } | |
139 | |
140 void DiskCacheBasedQuicServerInfo::Persist() { | |
141 DCHECK(CalledOnValidThread()); | |
142 if (!IsReadyToPersist()) { | |
143 // Handle updates while a write is pending or if we haven't loaded from disk | |
144 // cache. Save the data to be written into a temporary buffer and then | |
145 // persist that data when we are ready to persist. | |
146 pending_write_data_ = Serialize(); | |
147 return; | |
148 } | |
149 PersistInternal(); | |
150 } | |
151 | |
152 void DiskCacheBasedQuicServerInfo::PersistInternal() { | |
153 DCHECK(CalledOnValidThread()); | |
154 DCHECK_NE(GET_BACKEND, state_); | |
155 DCHECK(new_data_.empty()); | |
156 CHECK(ready_); | |
157 DCHECK(wait_for_ready_callback_.is_null()); | |
158 | |
159 if (pending_write_data_.empty()) { | |
160 new_data_ = Serialize(); | |
161 } else { | |
162 new_data_ = pending_write_data_; | |
163 base::STLClearObject(&pending_write_data_); | |
164 } | |
165 | |
166 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST); | |
167 if (!backend_) { | |
168 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE); | |
169 return; | |
170 } | |
171 | |
172 state_ = CREATE_OR_OPEN; | |
173 DoLoop(OK); | |
174 } | |
175 | |
176 void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() { | |
177 DCHECK(CalledOnValidThread()); | |
178 DCHECK_NE(GET_BACKEND, state_); | |
179 | |
180 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT); | |
181 if (!backend_) { | |
182 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE); | |
183 return; | |
184 } | |
185 | |
186 backend_->OnExternalCacheHit(key()); | |
187 } | |
188 | |
189 size_t DiskCacheBasedQuicServerInfo::EstimateMemoryUsage() const { | |
190 return base::trace_event::EstimateMemoryUsage(new_data_) + | |
191 base::trace_event::EstimateMemoryUsage(pending_write_data_) + | |
192 base::trace_event::EstimateMemoryUsage(server_id_) + | |
193 (read_buffer_ == nullptr ? 0 : read_buffer_->size()) + | |
194 (write_buffer_ == nullptr ? 0 : write_buffer_->size()) + | |
195 base::trace_event::EstimateMemoryUsage(data_); | |
196 } | |
197 | |
198 std::string DiskCacheBasedQuicServerInfo::key() const { | |
199 return "quicserverinfo:" + server_id_.ToString(); | |
200 } | |
201 | |
202 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused, | |
203 int rv) { | |
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 if (rv == OK) { | |
275 backend_ = data_shim_->backend; | |
276 state_ = OPEN; | |
277 } else { | |
278 RecordQuicServerInfoFailure(GET_BACKEND_FAILURE); | |
279 state_ = WAIT_FOR_DATA_READY_DONE; | |
280 } | |
281 return OK; | |
282 } | |
283 | |
284 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) { | |
285 if (rv == OK) { | |
286 entry_ = data_shim_->entry; | |
287 state_ = READ; | |
288 found_entry_ = true; | |
289 } else { | |
290 RecordQuicServerInfoFailure(OPEN_FAILURE); | |
291 state_ = WAIT_FOR_DATA_READY_DONE; | |
292 } | |
293 | |
294 return OK; | |
295 } | |
296 | |
297 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) { | |
298 if (rv > 0) | |
299 data_.assign(read_buffer_->data(), rv); | |
300 else if (rv < 0) | |
301 RecordQuicServerInfoFailure(READ_FAILURE); | |
302 | |
303 read_buffer_ = nullptr; | |
304 state_ = WAIT_FOR_DATA_READY_DONE; | |
305 return OK; | |
306 } | |
307 | |
308 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) { | |
309 if (rv < 0) | |
310 RecordQuicServerInfoFailure(WRITE_FAILURE); | |
311 write_buffer_ = nullptr; | |
312 state_ = SET_DONE; | |
313 return OK; | |
314 } | |
315 | |
316 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) { | |
317 if (rv != OK) { | |
318 RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE); | |
319 state_ = SET_DONE; | |
320 } else { | |
321 if (!entry_) { | |
322 entry_ = data_shim_->entry; | |
323 found_entry_ = true; | |
324 } | |
325 DCHECK(entry_); | |
326 state_ = WRITE; | |
327 } | |
328 return OK; | |
329 } | |
330 | |
331 int DiskCacheBasedQuicServerInfo::DoGetBackend() { | |
332 state_ = GET_BACKEND_COMPLETE; | |
333 return http_cache_->GetBackend(&data_shim_->backend, io_callback_); | |
334 } | |
335 | |
336 int DiskCacheBasedQuicServerInfo::DoOpen() { | |
337 state_ = OPEN_COMPLETE; | |
338 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); | |
339 } | |
340 | |
341 int DiskCacheBasedQuicServerInfo::DoRead() { | |
342 const int32_t size = entry_->GetDataSize(0 /* index */); | |
343 if (!size) { | |
344 state_ = WAIT_FOR_DATA_READY_DONE; | |
345 return OK; | |
346 } | |
347 | |
348 read_buffer_ = new IOBufferWithSize(size); | |
349 state_ = READ_COMPLETE; | |
350 return entry_->ReadData( | |
351 0 /* index */, 0 /* offset */, read_buffer_.get(), size, io_callback_); | |
352 } | |
353 | |
354 int DiskCacheBasedQuicServerInfo::DoWrite() { | |
355 write_buffer_ = new IOBufferWithSize(new_data_.size()); | |
356 memcpy(write_buffer_->data(), new_data_.data(), new_data_.size()); | |
357 state_ = WRITE_COMPLETE; | |
358 | |
359 return entry_->WriteData(0 /* index */, | |
360 0 /* offset */, | |
361 write_buffer_.get(), | |
362 new_data_.size(), | |
363 io_callback_, | |
364 true /* truncate */); | |
365 } | |
366 | |
367 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() { | |
368 state_ = CREATE_OR_OPEN_COMPLETE; | |
369 if (entry_) | |
370 return OK; | |
371 | |
372 if (found_entry_) { | |
373 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); | |
374 } | |
375 | |
376 return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_); | |
377 } | |
378 | |
379 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() { | |
380 DCHECK(!ready_); | |
381 state_ = NONE; | |
382 ready_ = true; | |
383 // We close the entry because, if we shutdown before ::Persist is called, | |
384 // then we might leak a cache reference, which causes a DCHECK on shutdown. | |
385 if (entry_) | |
386 entry_->Close(); | |
387 entry_ = NULL; | |
388 | |
389 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE); | |
390 if (!Parse(data_)) { | |
391 if (data_.empty()) | |
392 RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE); | |
393 else | |
394 RecordQuicServerInfoFailure(PARSE_FAILURE); | |
395 } | |
396 | |
397 UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime", | |
398 base::TimeTicks::Now() - load_start_time_); | |
399 return OK; | |
400 } | |
401 | |
402 int DiskCacheBasedQuicServerInfo::DoSetDone() { | |
403 if (entry_) | |
404 entry_->Close(); | |
405 entry_ = NULL; | |
406 base::STLClearObject(&new_data_); | |
407 state_ = NONE; | |
408 return OK; | |
409 } | |
410 | |
411 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus( | |
412 QuicServerInfoAPICall call) { | |
413 if (!backend_) { | |
414 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call, | |
415 QUIC_SERVER_INFO_NUM_OF_API_CALLS); | |
416 } else if (backend_->GetCacheType() == MEMORY_CACHE) { | |
417 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call, | |
418 QUIC_SERVER_INFO_NUM_OF_API_CALLS); | |
419 } else { | |
420 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call, | |
421 QUIC_SERVER_INFO_NUM_OF_API_CALLS); | |
422 } | |
423 } | |
424 | |
425 void DiskCacheBasedQuicServerInfo::RecordLastFailure() { | |
426 if (last_failure_ != NO_FAILURE) { | |
427 UMA_HISTOGRAM_ENUMERATION( | |
428 "Net.QuicDiskCache.FailureReason.WaitForDataReady", | |
429 last_failure_, NUM_OF_FAILURES); | |
430 } | |
431 last_failure_ = NO_FAILURE; | |
432 } | |
433 | |
434 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure( | |
435 FailureReason failure) { | |
436 last_failure_ = failure; | |
437 | |
438 if (!backend_) { | |
439 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend", | |
440 failure, NUM_OF_FAILURES); | |
441 } else if (backend_->GetCacheType() == MEMORY_CACHE) { | |
442 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache", | |
443 failure, NUM_OF_FAILURES); | |
444 } else { | |
445 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache", | |
446 failure, NUM_OF_FAILURES); | |
447 } | |
448 } | |
449 | |
450 } // namespace net | |
OLD | NEW |