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/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "net/base/completion_callback.h"
12 #include "net/base/io_buffer.h"
13 #include "net/base/net_errors.h"
14 #include "net/http/http_cache.h"
15 #include "net/http/http_network_session.h"
16 #include "net/quic/quic_server_id.h"
20 // Some APIs inside disk_cache take a handle that the caller must keep alive
21 // until the API has finished its asynchronous execution.
23 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the
24 // operation completes causing a use-after-free.
26 // This data shim struct is meant to provide a location for the disk_cache
27 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo
28 // object has been deleted. The lifetime for instances of this struct
29 // should be bound to the CompletionCallback that is passed to the disk_cache
30 // API. We do this by binding an instance of this struct to an unused
31 // parameter for OnIOComplete() using base::Owned().
33 // This is a hack. A better fix is to make it so that the disk_cache APIs
34 // take a Callback to a mutator for setting the output value rather than
35 // writing into a raw handle. Then the caller can just pass in a Callback
36 // bound to WeakPtr for itself. This callback would correctly "no-op" itself
37 // when the DiskCacheBasedQuicServerInfo object is deleted.
39 // TODO(ajwong): Change disk_cache's API to return results via Callback.
40 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim
{
41 CacheOperationDataShim() : backend(NULL
), entry(NULL
) {}
43 disk_cache::Backend
* backend
;
44 disk_cache::Entry
* entry
;
47 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
48 const QuicServerId
& server_id
,
49 HttpCache
* http_cache
)
50 : QuicServerInfo(server_id
),
51 data_shim_(new CacheOperationDataShim()),
55 server_id_(server_id
),
56 http_cache_(http_cache
),
61 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete
,
62 weak_factory_
.GetWeakPtr(),
63 base::Owned(data_shim_
)); // Ownership assigned.
66 void DiskCacheBasedQuicServerInfo::Start() {
67 DCHECK(CalledOnValidThread());
68 DCHECK_EQ(GET_BACKEND
, state_
);
69 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START
);
70 load_start_time_
= base::TimeTicks::Now();
74 int DiskCacheBasedQuicServerInfo::WaitForDataReady(
75 const CompletionCallback
& callback
) {
76 DCHECK(CalledOnValidThread());
77 DCHECK_NE(GET_BACKEND
, state_
);
79 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY
);
83 if (!callback
.is_null()) {
84 // Prevent a new callback for WaitForDataReady overwriting an existing
85 // pending callback (|user_callback_|).
86 // TODO(rtenneti): Rename user_callback_ as wait_for_ready_callback_.
87 if (!user_callback_
.is_null()) {
88 RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE
);
89 return ERR_INVALID_ARGUMENT
;
91 user_callback_
= callback
;
94 return ERR_IO_PENDING
;
97 void DiskCacheBasedQuicServerInfo::CancelWaitForDataReadyCallback() {
98 DCHECK(CalledOnValidThread());
100 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL
);
101 if (!user_callback_
.is_null())
102 user_callback_
.Reset();
105 bool DiskCacheBasedQuicServerInfo::IsDataReady() {
109 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() {
110 // The data can be persisted if it has been loaded from the disk cache
111 // and there are no pending writes.
112 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST
);
113 if (ready_
&& new_data_
.empty())
115 RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE
);
119 void DiskCacheBasedQuicServerInfo::Persist() {
120 DCHECK(CalledOnValidThread());
121 if (!IsReadyToPersist()) {
122 // Handle updates while a write is pending or if we haven't loaded from disk
123 // cache. Save the data to be written into a temporary buffer and then
124 // persist that data when we are ready to persist.
125 pending_write_data_
= Serialize();
131 void DiskCacheBasedQuicServerInfo::PersistInternal() {
132 DCHECK(CalledOnValidThread());
133 DCHECK_NE(GET_BACKEND
, state_
);
135 DCHECK(new_data_
.empty());
137 DCHECK(user_callback_
.is_null());
138 if (pending_write_data_
.empty()) {
139 new_data_
= Serialize();
141 new_data_
= pending_write_data_
;
142 pending_write_data_
.clear();
145 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST
);
147 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE
);
151 state_
= CREATE_OR_OPEN
;
155 void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() {
156 DCHECK(CalledOnValidThread());
157 DCHECK_NE(GET_BACKEND
, state_
);
159 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT
);
161 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE
);
165 backend_
->OnExternalCacheHit(key());
168 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
169 DCHECK(user_callback_
.is_null());
174 std::string
DiskCacheBasedQuicServerInfo::key() const {
175 return "quicserverinfo:" + server_id_
.ToString();
178 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim
* unused
,
180 DCHECK_NE(NONE
, state_
);
182 if (rv
== ERR_IO_PENDING
)
184 if (!user_callback_
.is_null()) {
185 CompletionCallback callback
= user_callback_
;
186 user_callback_
.Reset();
189 if (ready_
&& !pending_write_data_
.empty()) {
190 DCHECK_EQ(NONE
, state_
);
195 int DiskCacheBasedQuicServerInfo::DoLoop(int rv
) {
201 case GET_BACKEND_COMPLETE
:
202 rv
= DoGetBackendComplete(rv
);
208 rv
= DoOpenComplete(rv
);
214 rv
= DoReadComplete(rv
);
216 case WAIT_FOR_DATA_READY_DONE
:
217 rv
= DoWaitForDataReadyDone();
220 rv
= DoCreateOrOpen();
222 case CREATE_OR_OPEN_COMPLETE
:
223 rv
= DoCreateOrOpenComplete(rv
);
229 rv
= DoWriteComplete(rv
);
238 } while (rv
!= ERR_IO_PENDING
&& state_
!= NONE
);
243 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv
) {
245 backend_
= data_shim_
->backend
;
248 RecordQuicServerInfoFailure(GET_BACKEND_FAILURE
);
249 state_
= WAIT_FOR_DATA_READY_DONE
;
254 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv
) {
256 entry_
= data_shim_
->entry
;
260 RecordQuicServerInfoFailure(OPEN_FAILURE
);
261 state_
= WAIT_FOR_DATA_READY_DONE
;
267 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv
) {
269 data_
.assign(read_buffer_
->data(), rv
);
271 RecordQuicServerInfoFailure(READ_FAILURE
);
273 state_
= WAIT_FOR_DATA_READY_DONE
;
277 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv
) {
279 RecordQuicServerInfoFailure(WRITE_FAILURE
);
284 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv
) {
286 RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE
);
290 entry_
= data_shim_
->entry
;
299 int DiskCacheBasedQuicServerInfo::DoGetBackend() {
300 state_
= GET_BACKEND_COMPLETE
;
301 return http_cache_
->GetBackend(&data_shim_
->backend
, io_callback_
);
304 int DiskCacheBasedQuicServerInfo::DoOpen() {
305 state_
= OPEN_COMPLETE
;
306 return backend_
->OpenEntry(key(), &data_shim_
->entry
, io_callback_
);
309 int DiskCacheBasedQuicServerInfo::DoRead() {
310 const int32 size
= entry_
->GetDataSize(0 /* index */);
312 state_
= WAIT_FOR_DATA_READY_DONE
;
316 read_buffer_
= new IOBuffer(size
);
317 state_
= READ_COMPLETE
;
318 return entry_
->ReadData(
319 0 /* index */, 0 /* offset */, read_buffer_
.get(), size
, io_callback_
);
322 int DiskCacheBasedQuicServerInfo::DoWrite() {
323 write_buffer_
= new IOBuffer(new_data_
.size());
324 memcpy(write_buffer_
->data(), new_data_
.data(), new_data_
.size());
325 state_
= WRITE_COMPLETE
;
327 return entry_
->WriteData(0 /* index */,
332 true /* truncate */);
335 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
336 state_
= CREATE_OR_OPEN_COMPLETE
;
341 return backend_
->OpenEntry(key(), &data_shim_
->entry
, io_callback_
);
344 return backend_
->CreateEntry(key(), &data_shim_
->entry
, io_callback_
);
347 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
351 // We close the entry because, if we shutdown before ::Persist is called,
352 // then we might leak a cache reference, which causes a DCHECK on shutdown.
357 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE
);
360 RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE
);
362 RecordQuicServerInfoFailure(PARSE_FAILURE
);
365 UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime",
366 base::TimeTicks::Now() - load_start_time_
);
370 int DiskCacheBasedQuicServerInfo::DoSetDone() {
379 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus(
380 QuicServerInfoAPICall call
) {
382 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call
,
383 QUIC_SERVER_INFO_NUM_OF_API_CALLS
);
384 } else if (backend_
->GetCacheType() == net::MEMORY_CACHE
) {
385 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call
,
386 QUIC_SERVER_INFO_NUM_OF_API_CALLS
);
388 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call
,
389 QUIC_SERVER_INFO_NUM_OF_API_CALLS
);
393 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure(
394 FailureReason failure
) {
396 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend",
397 failure
, NUM_OF_FAILURES
);
398 } else if (backend_
->GetCacheType() == net::MEMORY_CACHE
) {
399 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache",
400 failure
, NUM_OF_FAILURES
);
402 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache",
403 failure
, NUM_OF_FAILURES
);