Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(635)

Unified Diff: net/http/disk_cache_based_quic_server_info.cc

Issue 2821053004: Revert of Remove the code to store and load QUIC server configs in the disk cache. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/http/disk_cache_based_quic_server_info.cc
diff --git a/net/http/disk_cache_based_quic_server_info.cc b/net/http/disk_cache_based_quic_server_info.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b149fc95ae211e39a78ef9fa35e31add15508f28
--- /dev/null
+++ b/net/http/disk_cache_based_quic_server_info.cc
@@ -0,0 +1,450 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/disk_cache_based_quic_server_info.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/trace_event/memory_usage_estimator.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_network_session.h"
+#include "net/quic/core/quic_server_id.h"
+
+namespace net {
+
+// Some APIs inside disk_cache take a handle that the caller must keep alive
+// until the API has finished its asynchronous execution.
+//
+// Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the
+// operation completes causing a use-after-free.
+//
+// This data shim struct is meant to provide a location for the disk_cache
+// APIs to write into even if the originating DiskCacheBasedQuicServerInfo
+// object has been deleted. The lifetime for instances of this struct
+// should be bound to the CompletionCallback that is passed to the disk_cache
+// API. We do this by binding an instance of this struct to an unused
+// parameter for OnIOComplete() using base::Owned().
+//
+// This is a hack. A better fix is to make it so that the disk_cache APIs
+// take a Callback to a mutator for setting the output value rather than
+// writing into a raw handle. Then the caller can just pass in a Callback
+// bound to WeakPtr for itself. This callback would correctly "no-op" itself
+// when the DiskCacheBasedQuicServerInfo object is deleted.
+//
+// TODO(ajwong): Change disk_cache's API to return results via Callback.
+struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim {
+ CacheOperationDataShim() : backend(NULL), entry(NULL) {}
+
+ disk_cache::Backend* backend;
+ disk_cache::Entry* entry;
+};
+
+DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
+ const QuicServerId& server_id,
+ HttpCache* http_cache)
+ : QuicServerInfo(server_id),
+ data_shim_(new CacheOperationDataShim()),
+ state_(GET_BACKEND),
+ ready_(false),
+ found_entry_(false),
+ server_id_(server_id),
+ http_cache_(http_cache),
+ backend_(NULL),
+ entry_(NULL),
+ last_failure_(NO_FAILURE),
+ weak_factory_(this) {
+ io_callback_ =
+ base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete,
+ weak_factory_.GetWeakPtr(),
+ base::Owned(data_shim_)); // Ownership assigned.
+}
+
+DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
+ DCHECK(wait_for_ready_callback_.is_null());
+ if (entry_)
+ entry_->Close();
+}
+
+void DiskCacheBasedQuicServerInfo::Start() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(GET_BACKEND, state_);
+ DCHECK_EQ(last_failure_, NO_FAILURE);
+ RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START);
+ load_start_time_ = base::TimeTicks::Now();
+ DoLoop(OK);
+}
+
+int DiskCacheBasedQuicServerInfo::WaitForDataReady(
+ const CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(GET_BACKEND, state_);
+ wait_for_data_start_time_ = base::TimeTicks::Now();
+
+ RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY);
+ if (ready_) {
+ wait_for_data_end_time_ = base::TimeTicks::Now();
+ RecordLastFailure();
+ return OK;
+ }
+
+ if (!callback.is_null()) {
+ // Prevent a new callback for WaitForDataReady overwriting an existing
+ // pending callback (|wait_for_ready_callback_|).
+ if (!wait_for_ready_callback_.is_null()) {
+ RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE);
+ return ERR_INVALID_ARGUMENT;
+ }
+ wait_for_ready_callback_ = callback;
+ }
+
+ return ERR_IO_PENDING;
+}
+
+void DiskCacheBasedQuicServerInfo::ResetWaitForDataReadyCallback() {
+ DCHECK(CalledOnValidThread());
+ wait_for_ready_callback_.Reset();
+}
+
+void DiskCacheBasedQuicServerInfo::CancelWaitForDataReadyCallback() {
+ DCHECK(CalledOnValidThread());
+
+ RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL);
+ if (!wait_for_ready_callback_.is_null()) {
+ RecordLastFailure();
+ wait_for_ready_callback_.Reset();
+ }
+}
+
+bool DiskCacheBasedQuicServerInfo::IsDataReady() {
+ return ready_;
+}
+
+bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() {
+ // The data can be persisted if it has been loaded from the disk cache
+ // and there are no pending writes.
+ RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST);
+ if (ready_ && new_data_.empty())
+ return true;
+ RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE);
+ return false;
+}
+
+void DiskCacheBasedQuicServerInfo::Persist() {
+ DCHECK(CalledOnValidThread());
+ if (!IsReadyToPersist()) {
+ // Handle updates while a write is pending or if we haven't loaded from disk
+ // cache. Save the data to be written into a temporary buffer and then
+ // persist that data when we are ready to persist.
+ pending_write_data_ = Serialize();
+ return;
+ }
+ PersistInternal();
+}
+
+void DiskCacheBasedQuicServerInfo::PersistInternal() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(GET_BACKEND, state_);
+ DCHECK(new_data_.empty());
+ CHECK(ready_);
+ DCHECK(wait_for_ready_callback_.is_null());
+
+ if (pending_write_data_.empty()) {
+ new_data_ = Serialize();
+ } else {
+ new_data_ = pending_write_data_;
+ base::STLClearObject(&pending_write_data_);
+ }
+
+ RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST);
+ if (!backend_) {
+ RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE);
+ return;
+ }
+
+ state_ = CREATE_OR_OPEN;
+ DoLoop(OK);
+}
+
+void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(GET_BACKEND, state_);
+
+ RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT);
+ if (!backend_) {
+ RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE);
+ return;
+ }
+
+ backend_->OnExternalCacheHit(key());
+}
+
+size_t DiskCacheBasedQuicServerInfo::EstimateMemoryUsage() const {
+ return base::trace_event::EstimateMemoryUsage(new_data_) +
+ base::trace_event::EstimateMemoryUsage(pending_write_data_) +
+ base::trace_event::EstimateMemoryUsage(server_id_) +
+ (read_buffer_ == nullptr ? 0 : read_buffer_->size()) +
+ (write_buffer_ == nullptr ? 0 : write_buffer_->size()) +
+ base::trace_event::EstimateMemoryUsage(data_);
+}
+
+std::string DiskCacheBasedQuicServerInfo::key() const {
+ return "quicserverinfo:" + server_id_.ToString();
+}
+
+void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused,
+ int rv) {
+ DCHECK_NE(NONE, state_);
+ rv = DoLoop(rv);
+ if (rv == ERR_IO_PENDING)
+ return;
+
+ base::WeakPtr<DiskCacheBasedQuicServerInfo> weak_this =
+ weak_factory_.GetWeakPtr();
+
+ if (!wait_for_ready_callback_.is_null()) {
+ wait_for_data_end_time_ = base::TimeTicks::Now();
+ RecordLastFailure();
+ base::ResetAndReturn(&wait_for_ready_callback_).Run(rv);
+ }
+ // |wait_for_ready_callback_| could delete the object if there is an error.
+ // Check if |weak_this| still exists before accessing it.
+ if (weak_this.get() && ready_ && !pending_write_data_.empty()) {
+ DCHECK_EQ(NONE, state_);
+ PersistInternal();
+ }
+}
+
+int DiskCacheBasedQuicServerInfo::DoLoop(int rv) {
+ do {
+ switch (state_) {
+ case GET_BACKEND:
+ rv = DoGetBackend();
+ break;
+ case GET_BACKEND_COMPLETE:
+ rv = DoGetBackendComplete(rv);
+ break;
+ case OPEN:
+ rv = DoOpen();
+ break;
+ case OPEN_COMPLETE:
+ rv = DoOpenComplete(rv);
+ break;
+ case READ:
+ rv = DoRead();
+ break;
+ case READ_COMPLETE:
+ rv = DoReadComplete(rv);
+ break;
+ case WAIT_FOR_DATA_READY_DONE:
+ rv = DoWaitForDataReadyDone();
+ break;
+ case CREATE_OR_OPEN:
+ rv = DoCreateOrOpen();
+ break;
+ case CREATE_OR_OPEN_COMPLETE:
+ rv = DoCreateOrOpenComplete(rv);
+ break;
+ case WRITE:
+ rv = DoWrite();
+ break;
+ case WRITE_COMPLETE:
+ rv = DoWriteComplete(rv);
+ break;
+ case SET_DONE:
+ rv = DoSetDone();
+ break;
+ default:
+ rv = OK;
+ NOTREACHED();
+ }
+ } while (rv != ERR_IO_PENDING && state_ != NONE);
+
+ return rv;
+}
+
+int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) {
+ if (rv == OK) {
+ backend_ = data_shim_->backend;
+ state_ = OPEN;
+ } else {
+ RecordQuicServerInfoFailure(GET_BACKEND_FAILURE);
+ state_ = WAIT_FOR_DATA_READY_DONE;
+ }
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) {
+ if (rv == OK) {
+ entry_ = data_shim_->entry;
+ state_ = READ;
+ found_entry_ = true;
+ } else {
+ RecordQuicServerInfoFailure(OPEN_FAILURE);
+ state_ = WAIT_FOR_DATA_READY_DONE;
+ }
+
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) {
+ if (rv > 0)
+ data_.assign(read_buffer_->data(), rv);
+ else if (rv < 0)
+ RecordQuicServerInfoFailure(READ_FAILURE);
+
+ read_buffer_ = nullptr;
+ state_ = WAIT_FOR_DATA_READY_DONE;
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) {
+ if (rv < 0)
+ RecordQuicServerInfoFailure(WRITE_FAILURE);
+ write_buffer_ = nullptr;
+ state_ = SET_DONE;
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) {
+ if (rv != OK) {
+ RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE);
+ state_ = SET_DONE;
+ } else {
+ if (!entry_) {
+ entry_ = data_shim_->entry;
+ found_entry_ = true;
+ }
+ DCHECK(entry_);
+ state_ = WRITE;
+ }
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoGetBackend() {
+ state_ = GET_BACKEND_COMPLETE;
+ return http_cache_->GetBackend(&data_shim_->backend, io_callback_);
+}
+
+int DiskCacheBasedQuicServerInfo::DoOpen() {
+ state_ = OPEN_COMPLETE;
+ return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
+}
+
+int DiskCacheBasedQuicServerInfo::DoRead() {
+ const int32_t size = entry_->GetDataSize(0 /* index */);
+ if (!size) {
+ state_ = WAIT_FOR_DATA_READY_DONE;
+ return OK;
+ }
+
+ read_buffer_ = new IOBufferWithSize(size);
+ state_ = READ_COMPLETE;
+ return entry_->ReadData(
+ 0 /* index */, 0 /* offset */, read_buffer_.get(), size, io_callback_);
+}
+
+int DiskCacheBasedQuicServerInfo::DoWrite() {
+ write_buffer_ = new IOBufferWithSize(new_data_.size());
+ memcpy(write_buffer_->data(), new_data_.data(), new_data_.size());
+ state_ = WRITE_COMPLETE;
+
+ return entry_->WriteData(0 /* index */,
+ 0 /* offset */,
+ write_buffer_.get(),
+ new_data_.size(),
+ io_callback_,
+ true /* truncate */);
+}
+
+int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
+ state_ = CREATE_OR_OPEN_COMPLETE;
+ if (entry_)
+ return OK;
+
+ if (found_entry_) {
+ return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
+ }
+
+ return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_);
+}
+
+int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
+ DCHECK(!ready_);
+ state_ = NONE;
+ ready_ = true;
+ // We close the entry because, if we shutdown before ::Persist is called,
+ // then we might leak a cache reference, which causes a DCHECK on shutdown.
+ if (entry_)
+ entry_->Close();
+ entry_ = NULL;
+
+ RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE);
+ if (!Parse(data_)) {
+ if (data_.empty())
+ RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE);
+ else
+ RecordQuicServerInfoFailure(PARSE_FAILURE);
+ }
+
+ UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime",
+ base::TimeTicks::Now() - load_start_time_);
+ return OK;
+}
+
+int DiskCacheBasedQuicServerInfo::DoSetDone() {
+ if (entry_)
+ entry_->Close();
+ entry_ = NULL;
+ base::STLClearObject(&new_data_);
+ state_ = NONE;
+ return OK;
+}
+
+void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus(
+ QuicServerInfoAPICall call) {
+ if (!backend_) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call,
+ QUIC_SERVER_INFO_NUM_OF_API_CALLS);
+ } else if (backend_->GetCacheType() == MEMORY_CACHE) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call,
+ QUIC_SERVER_INFO_NUM_OF_API_CALLS);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call,
+ QUIC_SERVER_INFO_NUM_OF_API_CALLS);
+ }
+}
+
+void DiskCacheBasedQuicServerInfo::RecordLastFailure() {
+ if (last_failure_ != NO_FAILURE) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Net.QuicDiskCache.FailureReason.WaitForDataReady",
+ last_failure_, NUM_OF_FAILURES);
+ }
+ last_failure_ = NO_FAILURE;
+}
+
+void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure(
+ FailureReason failure) {
+ last_failure_ = failure;
+
+ if (!backend_) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend",
+ failure, NUM_OF_FAILURES);
+ } else if (backend_->GetCacheType() == MEMORY_CACHE) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache",
+ failure, NUM_OF_FAILURES);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache",
+ failure, NUM_OF_FAILURES);
+ }
+}
+
+} // namespace net
« no previous file with comments | « net/http/disk_cache_based_quic_server_info.h ('k') | net/http/disk_cache_based_quic_server_info_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698