[Extensions Toolbar] Move tab-specific logic and increase test robustness
[chromium-blink-merge.git] / net / http / disk_cache_based_quic_server_info.cc
blobab34bbe9e1030db61a441ecd7e3c3bfd98e3558f
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.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "net/base/completion_callback.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 #include "net/http/http_cache.h"
17 #include "net/http/http_network_session.h"
18 #include "net/quic/quic_server_id.h"
20 namespace net {
22 // Some APIs inside disk_cache take a handle that the caller must keep alive
23 // until the API has finished its asynchronous execution.
25 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the
26 // operation completes causing a use-after-free.
28 // This data shim struct is meant to provide a location for the disk_cache
29 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo
30 // object has been deleted. The lifetime for instances of this struct
31 // should be bound to the CompletionCallback that is passed to the disk_cache
32 // API. We do this by binding an instance of this struct to an unused
33 // parameter for OnIOComplete() using base::Owned().
35 // This is a hack. A better fix is to make it so that the disk_cache APIs
36 // take a Callback to a mutator for setting the output value rather than
37 // writing into a raw handle. Then the caller can just pass in a Callback
38 // bound to WeakPtr for itself. This callback would correctly "no-op" itself
39 // when the DiskCacheBasedQuicServerInfo object is deleted.
41 // TODO(ajwong): Change disk_cache's API to return results via Callback.
42 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim {
43 CacheOperationDataShim() : backend(NULL), entry(NULL) {}
45 disk_cache::Backend* backend;
46 disk_cache::Entry* entry;
49 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
50 const QuicServerId& server_id,
51 HttpCache* http_cache)
52 : QuicServerInfo(server_id),
53 data_shim_(new CacheOperationDataShim()),
54 state_(GET_BACKEND),
55 ready_(false),
56 found_entry_(false),
57 server_id_(server_id),
58 http_cache_(http_cache),
59 backend_(NULL),
60 entry_(NULL),
61 last_failure_(NO_FAILURE),
62 weak_factory_(this) {
63 io_callback_ =
64 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete,
65 weak_factory_.GetWeakPtr(),
66 base::Owned(data_shim_)); // Ownership assigned.
69 void DiskCacheBasedQuicServerInfo::Start() {
70 DCHECK(CalledOnValidThread());
71 DCHECK_EQ(GET_BACKEND, state_);
72 DCHECK_EQ(last_failure_, NO_FAILURE);
73 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START);
74 load_start_time_ = base::TimeTicks::Now();
75 DoLoop(OK);
78 int DiskCacheBasedQuicServerInfo::WaitForDataReady(
79 const CompletionCallback& callback) {
80 DCHECK(CalledOnValidThread());
81 DCHECK_NE(GET_BACKEND, state_);
82 wait_for_data_start_time_ = base::TimeTicks::Now();
84 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY);
85 if (ready_) {
86 wait_for_data_end_time_ = base::TimeTicks::Now();
87 RecordLastFailure();
88 return OK;
91 if (!callback.is_null()) {
92 // Prevent a new callback for WaitForDataReady overwriting an existing
93 // pending callback (|wait_for_ready_callback_|).
94 if (!wait_for_ready_callback_.is_null()) {
95 RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE);
96 return ERR_INVALID_ARGUMENT;
98 wait_for_ready_callback_ = callback;
101 return ERR_IO_PENDING;
104 void DiskCacheBasedQuicServerInfo::CancelWaitForDataReadyCallback() {
105 DCHECK(CalledOnValidThread());
107 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL);
108 if (!wait_for_ready_callback_.is_null()) {
109 RecordLastFailure();
110 wait_for_ready_callback_.Reset();
114 bool DiskCacheBasedQuicServerInfo::IsDataReady() {
115 return ready_;
118 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() {
119 // The data can be persisted if it has been loaded from the disk cache
120 // and there are no pending writes.
121 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST);
122 if (ready_ && new_data_.empty())
123 return true;
124 RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE);
125 return false;
128 void DiskCacheBasedQuicServerInfo::Persist() {
129 DCHECK(CalledOnValidThread());
130 if (!IsReadyToPersist()) {
131 // Handle updates while a write is pending or if we haven't loaded from disk
132 // cache. Save the data to be written into a temporary buffer and then
133 // persist that data when we are ready to persist.
134 pending_write_data_ = Serialize();
135 return;
137 PersistInternal();
140 void DiskCacheBasedQuicServerInfo::PersistInternal() {
141 DCHECK(CalledOnValidThread());
142 DCHECK_NE(GET_BACKEND, state_);
143 DCHECK(new_data_.empty());
144 CHECK(ready_);
145 DCHECK(wait_for_ready_callback_.is_null());
147 if (pending_write_data_.empty()) {
148 new_data_ = Serialize();
149 } else {
150 new_data_ = pending_write_data_;
151 pending_write_data_.clear();
154 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST);
155 if (!backend_) {
156 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE);
157 return;
160 state_ = CREATE_OR_OPEN;
161 DoLoop(OK);
164 void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() {
165 DCHECK(CalledOnValidThread());
166 DCHECK_NE(GET_BACKEND, state_);
168 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT);
169 if (!backend_) {
170 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE);
171 return;
174 backend_->OnExternalCacheHit(key());
177 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
178 DCHECK(wait_for_ready_callback_.is_null());
179 if (entry_)
180 entry_->Close();
183 std::string DiskCacheBasedQuicServerInfo::key() const {
184 return "quicserverinfo:" + server_id_.ToString();
187 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused,
188 int rv) {
189 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
190 tracked_objects::ScopedTracker tracking_profile(
191 FROM_HERE_WITH_EXPLICIT_FUNCTION(
192 "422516 DiskCacheBasedQuicServerInfo::OnIOComplete"));
194 DCHECK_NE(NONE, state_);
195 rv = DoLoop(rv);
196 if (rv == ERR_IO_PENDING)
197 return;
198 if (!wait_for_ready_callback_.is_null()) {
199 wait_for_data_end_time_ = base::TimeTicks::Now();
200 RecordLastFailure();
201 base::ResetAndReturn(&wait_for_ready_callback_).Run(rv);
203 if (ready_ && !pending_write_data_.empty()) {
204 DCHECK_EQ(NONE, state_);
205 PersistInternal();
209 int DiskCacheBasedQuicServerInfo::DoLoop(int rv) {
210 do {
211 switch (state_) {
212 case GET_BACKEND:
213 rv = DoGetBackend();
214 break;
215 case GET_BACKEND_COMPLETE:
216 rv = DoGetBackendComplete(rv);
217 break;
218 case OPEN:
219 rv = DoOpen();
220 break;
221 case OPEN_COMPLETE:
222 rv = DoOpenComplete(rv);
223 break;
224 case READ:
225 rv = DoRead();
226 break;
227 case READ_COMPLETE:
228 rv = DoReadComplete(rv);
229 break;
230 case WAIT_FOR_DATA_READY_DONE:
231 rv = DoWaitForDataReadyDone();
232 break;
233 case CREATE_OR_OPEN:
234 rv = DoCreateOrOpen();
235 break;
236 case CREATE_OR_OPEN_COMPLETE:
237 rv = DoCreateOrOpenComplete(rv);
238 break;
239 case WRITE:
240 rv = DoWrite();
241 break;
242 case WRITE_COMPLETE:
243 rv = DoWriteComplete(rv);
244 break;
245 case SET_DONE:
246 rv = DoSetDone();
247 break;
248 default:
249 rv = OK;
250 NOTREACHED();
252 } while (rv != ERR_IO_PENDING && state_ != NONE);
254 return rv;
257 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) {
258 if (rv == OK) {
259 backend_ = data_shim_->backend;
260 state_ = OPEN;
261 } else {
262 RecordQuicServerInfoFailure(GET_BACKEND_FAILURE);
263 state_ = WAIT_FOR_DATA_READY_DONE;
265 return OK;
268 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) {
269 if (rv == OK) {
270 entry_ = data_shim_->entry;
271 state_ = READ;
272 found_entry_ = true;
273 } else {
274 RecordQuicServerInfoFailure(OPEN_FAILURE);
275 state_ = WAIT_FOR_DATA_READY_DONE;
278 return OK;
281 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) {
282 if (rv > 0)
283 data_.assign(read_buffer_->data(), rv);
284 else if (rv < 0)
285 RecordQuicServerInfoFailure(READ_FAILURE);
287 state_ = WAIT_FOR_DATA_READY_DONE;
288 return OK;
291 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) {
292 if (rv < 0)
293 RecordQuicServerInfoFailure(WRITE_FAILURE);
294 state_ = SET_DONE;
295 return OK;
298 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) {
299 if (rv != OK) {
300 RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE);
301 state_ = SET_DONE;
302 } else {
303 if (!entry_) {
304 entry_ = data_shim_->entry;
305 found_entry_ = true;
307 DCHECK(entry_);
308 state_ = WRITE;
310 return OK;
313 int DiskCacheBasedQuicServerInfo::DoGetBackend() {
314 state_ = GET_BACKEND_COMPLETE;
315 return http_cache_->GetBackend(&data_shim_->backend, io_callback_);
318 int DiskCacheBasedQuicServerInfo::DoOpen() {
319 state_ = OPEN_COMPLETE;
320 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
323 int DiskCacheBasedQuicServerInfo::DoRead() {
324 const int32 size = entry_->GetDataSize(0 /* index */);
325 if (!size) {
326 state_ = WAIT_FOR_DATA_READY_DONE;
327 return OK;
330 read_buffer_ = new IOBuffer(size);
331 state_ = READ_COMPLETE;
332 return entry_->ReadData(
333 0 /* index */, 0 /* offset */, read_buffer_.get(), size, io_callback_);
336 int DiskCacheBasedQuicServerInfo::DoWrite() {
337 write_buffer_ = new IOBuffer(new_data_.size());
338 memcpy(write_buffer_->data(), new_data_.data(), new_data_.size());
339 state_ = WRITE_COMPLETE;
341 return entry_->WriteData(0 /* index */,
342 0 /* offset */,
343 write_buffer_.get(),
344 new_data_.size(),
345 io_callback_,
346 true /* truncate */);
349 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
350 state_ = CREATE_OR_OPEN_COMPLETE;
351 if (entry_)
352 return OK;
354 if (found_entry_) {
355 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
358 return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_);
361 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
362 DCHECK(!ready_);
363 state_ = NONE;
364 ready_ = true;
365 // We close the entry because, if we shutdown before ::Persist is called,
366 // then we might leak a cache reference, which causes a DCHECK on shutdown.
367 if (entry_)
368 entry_->Close();
369 entry_ = NULL;
371 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE);
372 if (!Parse(data_)) {
373 if (data_.empty())
374 RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE);
375 else
376 RecordQuicServerInfoFailure(PARSE_FAILURE);
379 UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime",
380 base::TimeTicks::Now() - load_start_time_);
381 return OK;
384 int DiskCacheBasedQuicServerInfo::DoSetDone() {
385 if (entry_)
386 entry_->Close();
387 entry_ = NULL;
388 new_data_.clear();
389 state_ = NONE;
390 return OK;
393 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus(
394 QuicServerInfoAPICall call) {
395 if (!backend_) {
396 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call,
397 QUIC_SERVER_INFO_NUM_OF_API_CALLS);
398 } else if (backend_->GetCacheType() == net::MEMORY_CACHE) {
399 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call,
400 QUIC_SERVER_INFO_NUM_OF_API_CALLS);
401 } else {
402 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call,
403 QUIC_SERVER_INFO_NUM_OF_API_CALLS);
407 void DiskCacheBasedQuicServerInfo::RecordLastFailure() {
408 if (last_failure_ != NO_FAILURE) {
409 UMA_HISTOGRAM_ENUMERATION(
410 "Net.QuicDiskCache.FailureReason.WaitForDataReady",
411 last_failure_, NUM_OF_FAILURES);
413 last_failure_ = NO_FAILURE;
416 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure(
417 FailureReason failure) {
418 last_failure_ = failure;
420 if (!backend_) {
421 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend",
422 failure, NUM_OF_FAILURES);
423 } else if (backend_->GetCacheType() == net::MEMORY_CACHE) {
424 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache",
425 failure, NUM_OF_FAILURES);
426 } else {
427 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache",
428 failure, NUM_OF_FAILURES);
432 } // namespace net