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 // Histogram for tracking down the state of disk_cache::Entry.
21 enum DiskCacheEntryState
{
22 DISK_CACHE_ENTRY_OPENED
= 0,
23 DISK_CACHE_ENTRY_CLOSED
= 1,
24 DISK_CACHE_ENTRY_NUM_STATES
= 2,
27 void RecordDiskCacheEntryState(DiskCacheEntryState entry_state
) {
28 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.EntryState", entry_state
,
29 DISK_CACHE_ENTRY_NUM_STATES
);
32 // Some APIs inside disk_cache take a handle that the caller must keep alive
33 // until the API has finished its asynchronous execution.
35 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the
36 // operation completes causing a use-after-free.
38 // This data shim struct is meant to provide a location for the disk_cache
39 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo
40 // object has been deleted. The lifetime for instances of this struct
41 // should be bound to the CompletionCallback that is passed to the disk_cache
42 // API. We do this by binding an instance of this struct to an unused
43 // parameter for OnIOComplete() using base::Owned().
45 // This is a hack. A better fix is to make it so that the disk_cache APIs
46 // take a Callback to a mutator for setting the output value rather than
47 // writing into a raw handle. Then the caller can just pass in a Callback
48 // bound to WeakPtr for itself. This callback would correctly "no-op" itself
49 // when the DiskCacheBasedQuicServerInfo object is deleted.
51 // TODO(ajwong): Change disk_cache's API to return results via Callback.
52 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim
{
53 CacheOperationDataShim() : backend(NULL
), entry(NULL
) {}
55 disk_cache::Backend
* backend
;
56 disk_cache::Entry
* entry
;
59 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
60 const QuicServerId
& server_id
,
61 HttpCache
* http_cache
)
62 : QuicServerInfo(server_id
),
63 data_shim_(new CacheOperationDataShim()),
67 server_id_(server_id
),
68 http_cache_(http_cache
),
73 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete
,
74 weak_factory_
.GetWeakPtr(),
75 base::Owned(data_shim_
)); // Ownership assigned.
78 void DiskCacheBasedQuicServerInfo::Start() {
79 DCHECK(CalledOnValidThread());
80 DCHECK_EQ(GET_BACKEND
, state_
);
84 int DiskCacheBasedQuicServerInfo::WaitForDataReady(
85 const CompletionCallback
& callback
) {
86 DCHECK(CalledOnValidThread());
87 DCHECK_NE(GET_BACKEND
, state_
);
92 if (!callback
.is_null()) {
93 // Prevent a new callback for WaitForDataReady overwriting an existing
94 // pending callback (|user_callback_|).
95 if (!user_callback_
.is_null())
96 return ERR_INVALID_ARGUMENT
;
97 user_callback_
= callback
;
100 return ERR_IO_PENDING
;
103 bool DiskCacheBasedQuicServerInfo::IsDataReady() {
107 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() {
108 // TODO(rtenneti): Handle updates while a write is pending. Change
109 // Persist() to save the data to be written into a temporary buffer
110 // and then persist that data when we are ready to persist.
112 // The data can be persisted if it has been loaded from the disk cache
113 // and there are no pending writes.
114 return ready_
&& new_data_
.empty();
117 void DiskCacheBasedQuicServerInfo::Persist() {
118 DCHECK(CalledOnValidThread());
119 DCHECK_NE(GET_BACKEND
, state_
);
121 DCHECK(new_data_
.empty());
123 DCHECK(user_callback_
.is_null());
124 new_data_
= Serialize();
129 state_
= CREATE_OR_OPEN
;
133 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
134 DCHECK(user_callback_
.is_null());
137 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_CLOSED
);
141 std::string
DiskCacheBasedQuicServerInfo::key() const {
142 return "quicserverinfo:" + server_id_
.ToString();
145 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim
* unused
,
147 DCHECK_NE(NONE
, state_
);
149 if (rv
!= ERR_IO_PENDING
&& !user_callback_
.is_null()) {
150 CompletionCallback callback
= user_callback_
;
151 user_callback_
.Reset();
156 int DiskCacheBasedQuicServerInfo::DoLoop(int rv
) {
162 case GET_BACKEND_COMPLETE
:
163 rv
= DoGetBackendComplete(rv
);
169 rv
= DoOpenComplete(rv
);
175 rv
= DoReadComplete(rv
);
177 case WAIT_FOR_DATA_READY_DONE
:
178 rv
= DoWaitForDataReadyDone();
181 rv
= DoCreateOrOpen();
183 case CREATE_OR_OPEN_COMPLETE
:
184 rv
= DoCreateOrOpenComplete(rv
);
190 rv
= DoWriteComplete(rv
);
199 } while (rv
!= ERR_IO_PENDING
&& state_
!= NONE
);
204 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv
) {
206 backend_
= data_shim_
->backend
;
209 state_
= WAIT_FOR_DATA_READY_DONE
;
214 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv
) {
216 entry_
= data_shim_
->entry
;
219 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_OPENED
);
221 state_
= WAIT_FOR_DATA_READY_DONE
;
227 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv
) {
229 data_
.assign(read_buffer_
->data(), rv
);
231 state_
= WAIT_FOR_DATA_READY_DONE
;
235 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv
) {
240 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv
) {
245 entry_
= data_shim_
->entry
;
247 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_OPENED
);
255 int DiskCacheBasedQuicServerInfo::DoGetBackend() {
256 state_
= GET_BACKEND_COMPLETE
;
257 return http_cache_
->GetBackend(&data_shim_
->backend
, io_callback_
);
260 int DiskCacheBasedQuicServerInfo::DoOpen() {
261 state_
= OPEN_COMPLETE
;
262 return backend_
->OpenEntry(key(), &data_shim_
->entry
, io_callback_
);
265 int DiskCacheBasedQuicServerInfo::DoRead() {
266 const int32 size
= entry_
->GetDataSize(0 /* index */);
268 state_
= WAIT_FOR_DATA_READY_DONE
;
272 read_buffer_
= new IOBuffer(size
);
273 state_
= READ_COMPLETE
;
274 return entry_
->ReadData(
275 0 /* index */, 0 /* offset */, read_buffer_
.get(), size
, io_callback_
);
278 int DiskCacheBasedQuicServerInfo::DoWrite() {
279 write_buffer_
= new IOBuffer(new_data_
.size());
280 memcpy(write_buffer_
->data(), new_data_
.data(), new_data_
.size());
281 state_
= WRITE_COMPLETE
;
283 return entry_
->WriteData(0 /* index */,
288 true /* truncate */);
291 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
292 state_
= CREATE_OR_OPEN_COMPLETE
;
297 return backend_
->OpenEntry(key(), &data_shim_
->entry
, io_callback_
);
300 return backend_
->CreateEntry(key(), &data_shim_
->entry
, io_callback_
);
303 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
307 // We close the entry because, if we shutdown before ::Persist is called,
308 // then we might leak a cache reference, which causes a DCHECK on shutdown.
311 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_CLOSED
);
318 int DiskCacheBasedQuicServerInfo::DoSetDone() {
321 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_CLOSED
);