Fix nullptr crash in OnEmbed
[chromium-blink-merge.git] / net / http / disk_cache_based_quic_server_info.cc
blobd019f9b35014820c47446e5d9003ad6584190863
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.
5 #include "net/http/disk_cache_based_quic_server_info.h"
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 "net/base/completion_callback.h"
13 #include "net/base/io_buffer.h"
14 #include "net/base/net_errors.h"
15 #include "net/http/http_cache.h"
16 #include "net/http/http_network_session.h"
17 #include "net/quic/quic_server_id.h"
19 namespace net {
21 // Some APIs inside disk_cache take a handle that the caller must keep alive
22 // until the API has finished its asynchronous execution.
24 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the
25 // operation completes causing a use-after-free.
27 // This data shim struct is meant to provide a location for the disk_cache
28 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo
29 // object has been deleted. The lifetime for instances of this struct
30 // should be bound to the CompletionCallback that is passed to the disk_cache
31 // API. We do this by binding an instance of this struct to an unused
32 // parameter for OnIOComplete() using base::Owned().
34 // This is a hack. A better fix is to make it so that the disk_cache APIs
35 // take a Callback to a mutator for setting the output value rather than
36 // writing into a raw handle. Then the caller can just pass in a Callback
37 // bound to WeakPtr for itself. This callback would correctly "no-op" itself
38 // when the DiskCacheBasedQuicServerInfo object is deleted.
40 // TODO(ajwong): Change disk_cache's API to return results via Callback.
41 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim {
42 CacheOperationDataShim() : backend(NULL), entry(NULL) {}
44 disk_cache::Backend* backend;
45 disk_cache::Entry* entry;
48 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
49 const QuicServerId& server_id,
50 HttpCache* http_cache)
51 : QuicServerInfo(server_id),
52 data_shim_(new CacheOperationDataShim()),
53 state_(GET_BACKEND),
54 ready_(false),
55 found_entry_(false),
56 server_id_(server_id),
57 http_cache_(http_cache),
58 backend_(NULL),
59 entry_(NULL),
60 last_failure_(NO_FAILURE),
61 weak_factory_(this) {
62 io_callback_ =
63 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete,
64 weak_factory_.GetWeakPtr(),
65 base::Owned(data_shim_)); // Ownership assigned.
68 void DiskCacheBasedQuicServerInfo::Start() {
69 DCHECK(CalledOnValidThread());
70 DCHECK_EQ(GET_BACKEND, state_);
71 DCHECK_EQ(last_failure_, NO_FAILURE);
72 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START);
73 load_start_time_ = base::TimeTicks::Now();
74 DoLoop(OK);
77 int DiskCacheBasedQuicServerInfo::WaitForDataReady(
78 const CompletionCallback& callback) {
79 DCHECK(CalledOnValidThread());
80 DCHECK_NE(GET_BACKEND, state_);
81 wait_for_data_start_time_ = base::TimeTicks::Now();
83 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY);
84 if (ready_) {
85 wait_for_data_end_time_ = base::TimeTicks::Now();
86 RecordLastFailure();
87 return OK;
90 if (!callback.is_null()) {
91 // Prevent a new callback for WaitForDataReady overwriting an existing
92 // pending callback (|wait_for_ready_callback_|).
93 if (!wait_for_ready_callback_.is_null()) {
94 RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE);
95 return ERR_INVALID_ARGUMENT;
97 wait_for_ready_callback_ = callback;
100 return ERR_IO_PENDING;
103 void DiskCacheBasedQuicServerInfo::ResetWaitForDataReadyCallback() {
104 DCHECK(CalledOnValidThread());
105 wait_for_ready_callback_.Reset();
108 void DiskCacheBasedQuicServerInfo::CancelWaitForDataReadyCallback() {
109 DCHECK(CalledOnValidThread());
111 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL);
112 if (!wait_for_ready_callback_.is_null()) {
113 RecordLastFailure();
114 wait_for_ready_callback_.Reset();
118 bool DiskCacheBasedQuicServerInfo::IsDataReady() {
119 return ready_;
122 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() {
123 // The data can be persisted if it has been loaded from the disk cache
124 // and there are no pending writes.
125 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST);
126 if (ready_ && new_data_.empty())
127 return true;
128 RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE);
129 return false;
132 void DiskCacheBasedQuicServerInfo::Persist() {
133 DCHECK(CalledOnValidThread());
134 if (!IsReadyToPersist()) {
135 // Handle updates while a write is pending or if we haven't loaded from disk
136 // cache. Save the data to be written into a temporary buffer and then
137 // persist that data when we are ready to persist.
138 pending_write_data_ = Serialize();
139 return;
141 PersistInternal();
144 void DiskCacheBasedQuicServerInfo::PersistInternal() {
145 DCHECK(CalledOnValidThread());
146 DCHECK_NE(GET_BACKEND, state_);
147 DCHECK(new_data_.empty());
148 CHECK(ready_);
149 DCHECK(wait_for_ready_callback_.is_null());
151 if (pending_write_data_.empty()) {
152 new_data_ = Serialize();
153 } else {
154 new_data_ = pending_write_data_;
155 pending_write_data_.clear();
158 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST);
159 if (!backend_) {
160 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE);
161 return;
164 state_ = CREATE_OR_OPEN;
165 DoLoop(OK);
168 void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() {
169 DCHECK(CalledOnValidThread());
170 DCHECK_NE(GET_BACKEND, state_);
172 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT);
173 if (!backend_) {
174 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE);
175 return;
178 backend_->OnExternalCacheHit(key());
181 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
182 DCHECK(wait_for_ready_callback_.is_null());
183 if (entry_)
184 entry_->Close();
187 std::string DiskCacheBasedQuicServerInfo::key() const {
188 return "quicserverinfo:" + server_id_.ToString();
191 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused,
192 int rv) {
193 DCHECK_NE(NONE, state_);
194 rv = DoLoop(rv);
195 if (rv == ERR_IO_PENDING)
196 return;
198 base::WeakPtr<DiskCacheBasedQuicServerInfo> weak_this =
199 weak_factory_.GetWeakPtr();
201 if (!wait_for_ready_callback_.is_null()) {
202 wait_for_data_end_time_ = base::TimeTicks::Now();
203 RecordLastFailure();
204 base::ResetAndReturn(&wait_for_ready_callback_).Run(rv);
206 // |wait_for_ready_callback_| could delete the object if there is an error.
207 // Check if |weak_this| still exists before accessing it.
208 if (weak_this.get() && ready_ && !pending_write_data_.empty()) {
209 DCHECK_EQ(NONE, state_);
210 PersistInternal();
214 int DiskCacheBasedQuicServerInfo::DoLoop(int rv) {
215 do {
216 switch (state_) {
217 case GET_BACKEND:
218 rv = DoGetBackend();
219 break;
220 case GET_BACKEND_COMPLETE:
221 rv = DoGetBackendComplete(rv);
222 break;
223 case OPEN:
224 rv = DoOpen();
225 break;
226 case OPEN_COMPLETE:
227 rv = DoOpenComplete(rv);
228 break;
229 case READ:
230 rv = DoRead();
231 break;
232 case READ_COMPLETE:
233 rv = DoReadComplete(rv);
234 break;
235 case WAIT_FOR_DATA_READY_DONE:
236 rv = DoWaitForDataReadyDone();
237 break;
238 case CREATE_OR_OPEN:
239 rv = DoCreateOrOpen();
240 break;
241 case CREATE_OR_OPEN_COMPLETE:
242 rv = DoCreateOrOpenComplete(rv);
243 break;
244 case WRITE:
245 rv = DoWrite();
246 break;
247 case WRITE_COMPLETE:
248 rv = DoWriteComplete(rv);
249 break;
250 case SET_DONE:
251 rv = DoSetDone();
252 break;
253 default:
254 rv = OK;
255 NOTREACHED();
257 } while (rv != ERR_IO_PENDING && state_ != NONE);
259 return rv;
262 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) {
263 if (rv == OK) {
264 backend_ = data_shim_->backend;
265 state_ = OPEN;
266 } else {
267 RecordQuicServerInfoFailure(GET_BACKEND_FAILURE);
268 state_ = WAIT_FOR_DATA_READY_DONE;
270 return OK;
273 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) {
274 if (rv == OK) {
275 entry_ = data_shim_->entry;
276 state_ = READ;
277 found_entry_ = true;
278 } else {
279 RecordQuicServerInfoFailure(OPEN_FAILURE);
280 state_ = WAIT_FOR_DATA_READY_DONE;
283 return OK;
286 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) {
287 if (rv > 0)
288 data_.assign(read_buffer_->data(), rv);
289 else if (rv < 0)
290 RecordQuicServerInfoFailure(READ_FAILURE);
292 state_ = WAIT_FOR_DATA_READY_DONE;
293 return OK;
296 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) {
297 if (rv < 0)
298 RecordQuicServerInfoFailure(WRITE_FAILURE);
299 state_ = SET_DONE;
300 return OK;
303 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) {
304 if (rv != OK) {
305 RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE);
306 state_ = SET_DONE;
307 } else {
308 if (!entry_) {
309 entry_ = data_shim_->entry;
310 found_entry_ = true;
312 DCHECK(entry_);
313 state_ = WRITE;
315 return OK;
318 int DiskCacheBasedQuicServerInfo::DoGetBackend() {
319 state_ = GET_BACKEND_COMPLETE;
320 return http_cache_->GetBackend(&data_shim_->backend, io_callback_);
323 int DiskCacheBasedQuicServerInfo::DoOpen() {
324 state_ = OPEN_COMPLETE;
325 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
328 int DiskCacheBasedQuicServerInfo::DoRead() {
329 const int32 size = entry_->GetDataSize(0 /* index */);
330 if (!size) {
331 state_ = WAIT_FOR_DATA_READY_DONE;
332 return OK;
335 read_buffer_ = new IOBuffer(size);
336 state_ = READ_COMPLETE;
337 return entry_->ReadData(
338 0 /* index */, 0 /* offset */, read_buffer_.get(), size, io_callback_);
341 int DiskCacheBasedQuicServerInfo::DoWrite() {
342 write_buffer_ = new IOBuffer(new_data_.size());
343 memcpy(write_buffer_->data(), new_data_.data(), new_data_.size());
344 state_ = WRITE_COMPLETE;
346 return entry_->WriteData(0 /* index */,
347 0 /* offset */,
348 write_buffer_.get(),
349 new_data_.size(),
350 io_callback_,
351 true /* truncate */);
354 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
355 state_ = CREATE_OR_OPEN_COMPLETE;
356 if (entry_)
357 return OK;
359 if (found_entry_) {
360 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
363 return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_);
366 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
367 DCHECK(!ready_);
368 state_ = NONE;
369 ready_ = true;
370 // We close the entry because, if we shutdown before ::Persist is called,
371 // then we might leak a cache reference, which causes a DCHECK on shutdown.
372 if (entry_)
373 entry_->Close();
374 entry_ = NULL;
376 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE);
377 if (!Parse(data_)) {
378 if (data_.empty())
379 RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE);
380 else
381 RecordQuicServerInfoFailure(PARSE_FAILURE);
384 UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime",
385 base::TimeTicks::Now() - load_start_time_);
386 return OK;
389 int DiskCacheBasedQuicServerInfo::DoSetDone() {
390 if (entry_)
391 entry_->Close();
392 entry_ = NULL;
393 new_data_.clear();
394 state_ = NONE;
395 return OK;
398 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus(
399 QuicServerInfoAPICall call) {
400 if (!backend_) {
401 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call,
402 QUIC_SERVER_INFO_NUM_OF_API_CALLS);
403 } else if (backend_->GetCacheType() == MEMORY_CACHE) {
404 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call,
405 QUIC_SERVER_INFO_NUM_OF_API_CALLS);
406 } else {
407 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call,
408 QUIC_SERVER_INFO_NUM_OF_API_CALLS);
412 void DiskCacheBasedQuicServerInfo::RecordLastFailure() {
413 if (last_failure_ != NO_FAILURE) {
414 UMA_HISTOGRAM_ENUMERATION(
415 "Net.QuicDiskCache.FailureReason.WaitForDataReady",
416 last_failure_, NUM_OF_FAILURES);
418 last_failure_ = NO_FAILURE;
421 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure(
422 FailureReason failure) {
423 last_failure_ = failure;
425 if (!backend_) {
426 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend",
427 failure, NUM_OF_FAILURES);
428 } else if (backend_->GetCacheType() == MEMORY_CACHE) {
429 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache",
430 failure, NUM_OF_FAILURES);
431 } else {
432 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache",
433 failure, NUM_OF_FAILURES);
437 } // namespace net