Support Promise<T> syntax in the IDL parser.
[chromium-blink-merge.git] / net / http / disk_cache_based_quic_server_info.cc
blobd55349e1bf906a053d1265c2006bf120448bc54c
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/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"
18 namespace net {
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()),
64 state_(GET_BACKEND),
65 ready_(false),
66 found_entry_(false),
67 server_id_(server_id),
68 http_cache_(http_cache),
69 backend_(NULL),
70 entry_(NULL),
71 weak_factory_(this) {
72 io_callback_ =
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_);
81 DoLoop(OK);
84 int DiskCacheBasedQuicServerInfo::WaitForDataReady(
85 const CompletionCallback& callback) {
86 DCHECK(CalledOnValidThread());
87 DCHECK_NE(GET_BACKEND, state_);
89 if (ready_)
90 return OK;
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() {
104 return ready_;
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());
122 CHECK(ready_);
123 DCHECK(user_callback_.is_null());
124 new_data_ = Serialize();
126 if (!backend_)
127 return;
129 state_ = CREATE_OR_OPEN;
130 DoLoop(OK);
133 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
134 DCHECK(user_callback_.is_null());
135 if (entry_) {
136 entry_->Close();
137 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_CLOSED);
141 std::string DiskCacheBasedQuicServerInfo::key() const {
142 return "quicserverinfo:" + server_id_.ToString();
145 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused,
146 int rv) {
147 DCHECK_NE(NONE, state_);
148 rv = DoLoop(rv);
149 if (rv != ERR_IO_PENDING && !user_callback_.is_null()) {
150 CompletionCallback callback = user_callback_;
151 user_callback_.Reset();
152 callback.Run(rv);
156 int DiskCacheBasedQuicServerInfo::DoLoop(int rv) {
157 do {
158 switch (state_) {
159 case GET_BACKEND:
160 rv = DoGetBackend();
161 break;
162 case GET_BACKEND_COMPLETE:
163 rv = DoGetBackendComplete(rv);
164 break;
165 case OPEN:
166 rv = DoOpen();
167 break;
168 case OPEN_COMPLETE:
169 rv = DoOpenComplete(rv);
170 break;
171 case READ:
172 rv = DoRead();
173 break;
174 case READ_COMPLETE:
175 rv = DoReadComplete(rv);
176 break;
177 case WAIT_FOR_DATA_READY_DONE:
178 rv = DoWaitForDataReadyDone();
179 break;
180 case CREATE_OR_OPEN:
181 rv = DoCreateOrOpen();
182 break;
183 case CREATE_OR_OPEN_COMPLETE:
184 rv = DoCreateOrOpenComplete(rv);
185 break;
186 case WRITE:
187 rv = DoWrite();
188 break;
189 case WRITE_COMPLETE:
190 rv = DoWriteComplete(rv);
191 break;
192 case SET_DONE:
193 rv = DoSetDone();
194 break;
195 default:
196 rv = OK;
197 NOTREACHED();
199 } while (rv != ERR_IO_PENDING && state_ != NONE);
201 return rv;
204 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) {
205 if (rv == OK) {
206 backend_ = data_shim_->backend;
207 state_ = OPEN;
208 } else {
209 state_ = WAIT_FOR_DATA_READY_DONE;
211 return OK;
214 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) {
215 if (rv == OK) {
216 entry_ = data_shim_->entry;
217 state_ = READ;
218 found_entry_ = true;
219 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_OPENED);
220 } else {
221 state_ = WAIT_FOR_DATA_READY_DONE;
224 return OK;
227 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) {
228 if (rv > 0)
229 data_.assign(read_buffer_->data(), rv);
231 state_ = WAIT_FOR_DATA_READY_DONE;
232 return OK;
235 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) {
236 state_ = SET_DONE;
237 return OK;
240 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) {
241 if (rv != OK) {
242 state_ = SET_DONE;
243 } else {
244 if (!entry_) {
245 entry_ = data_shim_->entry;
246 found_entry_ = true;
247 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_OPENED);
249 DCHECK(entry_);
250 state_ = WRITE;
252 return OK;
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 */);
267 if (!size) {
268 state_ = WAIT_FOR_DATA_READY_DONE;
269 return OK;
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 */,
284 0 /* offset */,
285 write_buffer_.get(),
286 new_data_.size(),
287 io_callback_,
288 true /* truncate */);
291 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
292 state_ = CREATE_OR_OPEN_COMPLETE;
293 if (entry_)
294 return OK;
296 if (found_entry_) {
297 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
300 return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_);
303 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
304 DCHECK(!ready_);
305 state_ = NONE;
306 ready_ = true;
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.
309 if (entry_) {
310 entry_->Close();
311 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_CLOSED);
313 entry_ = NULL;
314 Parse(data_);
315 return OK;
318 int DiskCacheBasedQuicServerInfo::DoSetDone() {
319 if (entry_) {
320 entry_->Close();
321 RecordDiskCacheEntryState(DISK_CACHE_ENTRY_CLOSED);
323 entry_ = NULL;
324 new_data_.clear();
325 state_ = NONE;
326 return OK;
329 } // namespace net