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"
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"
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()),
56 server_id_(server_id
),
57 http_cache_(http_cache
),
60 last_failure_(NO_FAILURE
),
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();
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
);
85 wait_for_data_end_time_
= base::TimeTicks::Now();
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()) {
114 wait_for_ready_callback_
.Reset();
118 bool DiskCacheBasedQuicServerInfo::IsDataReady() {
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())
128 RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE
);
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();
144 void DiskCacheBasedQuicServerInfo::PersistInternal() {
145 DCHECK(CalledOnValidThread());
146 DCHECK_NE(GET_BACKEND
, state_
);
147 DCHECK(new_data_
.empty());
149 DCHECK(wait_for_ready_callback_
.is_null());
151 if (pending_write_data_
.empty()) {
152 new_data_
= Serialize();
154 new_data_
= pending_write_data_
;
155 pending_write_data_
.clear();
158 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST
);
160 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE
);
164 state_
= CREATE_OR_OPEN
;
168 void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() {
169 DCHECK(CalledOnValidThread());
170 DCHECK_NE(GET_BACKEND
, state_
);
172 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT
);
174 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE
);
178 backend_
->OnExternalCacheHit(key());
181 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
182 DCHECK(wait_for_ready_callback_
.is_null());
187 std::string
DiskCacheBasedQuicServerInfo::key() const {
188 return "quicserverinfo:" + server_id_
.ToString();
191 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim
* unused
,
193 DCHECK_NE(NONE
, state_
);
195 if (rv
== ERR_IO_PENDING
)
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();
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_
);
214 int DiskCacheBasedQuicServerInfo::DoLoop(int rv
) {
220 case GET_BACKEND_COMPLETE
:
221 rv
= DoGetBackendComplete(rv
);
227 rv
= DoOpenComplete(rv
);
233 rv
= DoReadComplete(rv
);
235 case WAIT_FOR_DATA_READY_DONE
:
236 rv
= DoWaitForDataReadyDone();
239 rv
= DoCreateOrOpen();
241 case CREATE_OR_OPEN_COMPLETE
:
242 rv
= DoCreateOrOpenComplete(rv
);
248 rv
= DoWriteComplete(rv
);
257 } while (rv
!= ERR_IO_PENDING
&& state_
!= NONE
);
262 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv
) {
264 backend_
= data_shim_
->backend
;
267 RecordQuicServerInfoFailure(GET_BACKEND_FAILURE
);
268 state_
= WAIT_FOR_DATA_READY_DONE
;
273 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv
) {
275 entry_
= data_shim_
->entry
;
279 RecordQuicServerInfoFailure(OPEN_FAILURE
);
280 state_
= WAIT_FOR_DATA_READY_DONE
;
286 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv
) {
288 data_
.assign(read_buffer_
->data(), rv
);
290 RecordQuicServerInfoFailure(READ_FAILURE
);
292 state_
= WAIT_FOR_DATA_READY_DONE
;
296 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv
) {
298 RecordQuicServerInfoFailure(WRITE_FAILURE
);
303 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv
) {
305 RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE
);
309 entry_
= data_shim_
->entry
;
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 */);
331 state_
= WAIT_FOR_DATA_READY_DONE
;
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 */,
351 true /* truncate */);
354 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
355 state_
= CREATE_OR_OPEN_COMPLETE
;
360 return backend_
->OpenEntry(key(), &data_shim_
->entry
, io_callback_
);
363 return backend_
->CreateEntry(key(), &data_shim_
->entry
, io_callback_
);
366 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
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.
376 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE
);
379 RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE
);
381 RecordQuicServerInfoFailure(PARSE_FAILURE
);
384 UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime",
385 base::TimeTicks::Now() - load_start_time_
);
389 int DiskCacheBasedQuicServerInfo::DoSetDone() {
398 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus(
399 QuicServerInfoAPICall call
) {
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
);
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
;
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
);
432 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache",
433 failure
, NUM_OF_FAILURES
);