summaryrefslogtreecommitdiffstats
path: root/webkit/appcache/appcache_response.cc
diff options
context:
space:
mode:
authormichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-21 18:09:33 +0000
committermichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-21 18:09:33 +0000
commit073aabe9e48306216d979f03d6c2fc37d25d42e9 (patch)
treea9088a3c8be87fa57208074c7de65e998b865f94 /webkit/appcache/appcache_response.cc
parentc54d269e0b8a057533c1a1455a2acea0659629b9 (diff)
downloadchromium_src-073aabe9e48306216d979f03d6c2fc37d25d42e9.zip
chromium_src-073aabe9e48306216d979f03d6c2fc37d25d42e9.tar.gz
chromium_src-073aabe9e48306216d979f03d6c2fc37d25d42e9.tar.bz2
AppCacheResponse storage implementation
* classes AppCacheResponseReader and AppCacheResponseWriter * also implements AppCacheStorage.LoadResponseInfo() * using a memory-backed disk_cache for now, so not really on disk yet TEST=appcache_response_unittest.cc BUG=none Review URL: http://codereview.chromium.org/269062 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29670 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/appcache/appcache_response.cc')
-rw-r--r--webkit/appcache/appcache_response.cc262
1 files changed, 262 insertions, 0 deletions
diff --git a/webkit/appcache/appcache_response.cc b/webkit/appcache/appcache_response.cc
new file mode 100644
index 0000000..3952d59
--- /dev/null
+++ b/webkit/appcache/appcache_response.cc
@@ -0,0 +1,262 @@
+// Copyright (c) 2009 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 "webkit/appcache/appcache_response.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/pickle.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/io_buffer.h"
+#include "net/disk_cache/disk_cache.h"
+#include "webkit/appcache/appcache_service.h"
+
+using disk_cache::Entry;
+
+namespace appcache {
+
+namespace {
+
+// Disk cache entry data indices.
+enum {
+ kResponseInfoIndex,
+ kResponseContentIndex
+};
+
+// Disk cache entry keys.
+std::string response_key(int64 response_id) {
+ return Int64ToString(response_id);
+}
+
+// An IOBuffer that wraps a pickle's data. Ownership of the
+// pickle is transfered to the WrappedPickleIOBuffer object.
+class WrappedPickleIOBuffer : public net::WrappedIOBuffer {
+ public:
+ explicit WrappedPickleIOBuffer(const Pickle* pickle) :
+ net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle->data())),
+ pickle_(pickle) {
+ DCHECK(pickle->data());
+ }
+
+ private:
+ scoped_ptr<const Pickle> pickle_;
+};
+
+} // anon namespace
+
+
+// AppCacheResponseInfo ----------------------------------------------
+
+AppCacheResponseInfo::AppCacheResponseInfo(
+ AppCacheService* service, int64 response_id,
+ net::HttpResponseInfo* http_info)
+ : response_id_(response_id), http_response_info_(http_info),
+ service_(service) {
+ DCHECK(http_info);
+ DCHECK(response_id != kNoResponseId);
+ service_->storage()->working_set()->AddResponseInfo(this);
+}
+
+AppCacheResponseInfo::~AppCacheResponseInfo() {
+ service_->storage()->working_set()->RemoveResponseInfo(this);
+}
+
+
+// AppCacheResponseIO ----------------------------------------------
+
+AppCacheResponseIO::AppCacheResponseIO(
+ int64 response_id, disk_cache::Backend* disk_cache)
+ : response_id_(response_id), disk_cache_(disk_cache),
+ entry_(NULL), user_callback_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(raw_callback_(
+ new net::CancelableCompletionCallback<AppCacheResponseIO>(
+ this, &AppCacheResponseIO::OnRawIOComplete))) {
+}
+
+AppCacheResponseIO::~AppCacheResponseIO() {
+ raw_callback_->Cancel();
+ if (entry_)
+ entry_->Close();
+}
+
+void AppCacheResponseIO::ScheduleIOCompletionCallback(int result) {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &AppCacheResponseIO::OnIOComplete, result));
+}
+
+void AppCacheResponseIO::InvokeUserCompletionCallback(int result) {
+ // Clear the user callback and buffers prior to invoking the callback
+ // so the caller can schedule additional operations in the callback.
+ buffer_ = NULL;
+ info_buffer_ = NULL;
+ net::CompletionCallback* temp_user_callback = user_callback_;
+ user_callback_ = NULL;
+ temp_user_callback->Run(result);
+}
+
+void AppCacheResponseIO::ReadRaw(int index, int offset,
+ net::IOBuffer* buf, int buf_len) {
+ DCHECK(entry_);
+ raw_callback_->AddRef(); // Balanced in OnRawIOComplete.
+ int rv = entry_->ReadData(index, offset, buf, buf_len, raw_callback_);
+ if (rv != net::ERR_IO_PENDING) {
+ raw_callback_->Release();
+ ScheduleIOCompletionCallback(rv);
+ }
+}
+
+void AppCacheResponseIO::WriteRaw(int index, int offset,
+ net::IOBuffer* buf, int buf_len) {
+ DCHECK(entry_);
+ const bool kTruncate = true;
+ raw_callback_->AddRef(); // Balanced in OnRawIOComplete.
+ int rv = entry_->WriteData(index, offset, buf, buf_len, raw_callback_,
+ kTruncate);
+ if (rv != net::ERR_IO_PENDING) {
+ raw_callback_->Release();
+ ScheduleIOCompletionCallback(rv);
+ }
+}
+
+void AppCacheResponseIO::OnRawIOComplete(int result) {
+ raw_callback_->Release(); // Balance the AddRefs
+ OnIOComplete(result);
+}
+
+
+// AppCacheResponseReader ----------------------------------------------
+
+void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf,
+ net::CompletionCallback* callback) {
+ DCHECK(callback && !IsReadPending());
+ DCHECK(info_buf && !info_buf->http_info.get());
+ DCHECK(!buffer_.get() && !info_buffer_.get());
+
+ user_callback_ = callback; // cleared on completion
+
+ if (!OpenEntryIfNeeded()) {
+ ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
+ return;
+ }
+
+ int size = entry_->GetDataSize(kResponseInfoIndex);
+ info_buffer_ = info_buf;
+ buffer_ = new net::IOBuffer(size);
+ ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size);
+}
+
+void AppCacheResponseReader::ReadData(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback) {
+ DCHECK(callback && !IsReadPending());
+ DCHECK(buf && (buf_len >= 0));
+ DCHECK(!buffer_.get() && !info_buffer_.get());
+
+ user_callback_ = callback; // cleared on completion
+
+ if (!OpenEntryIfNeeded()) {
+ ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
+ return;
+ }
+
+ buffer_ = buf;
+ if (read_position_ + buf_len > range_length_) {
+ // TODO(michaeln): What about integer overflows?
+ DCHECK(range_length_ >= read_position_);
+ buf_len = range_length_ - read_position_;
+ }
+ ReadRaw(kResponseContentIndex, range_offset_ + read_position_,
+ buf, buf_len);
+}
+
+void AppCacheResponseReader::SetReadRange(int offset, int length) {
+ DCHECK(!IsReadPending() && !read_position_);
+ range_offset_ = offset;
+ range_length_ = length;
+}
+
+void AppCacheResponseReader::OnIOComplete(int result) {
+ if (result >= 0) {
+ if (info_buffer_.get()) {
+ // Allocate and deserialize the http info structure.
+ Pickle pickle(buffer_->data(), result);
+ bool response_truncated = false;
+ info_buffer_->http_info.reset(new net::HttpResponseInfo);
+ info_buffer_->http_info->InitFromPickle(pickle, &response_truncated);
+ DCHECK(!response_truncated);
+ } else {
+ read_position_ += result;
+ }
+ }
+ InvokeUserCompletionCallback(result);
+}
+
+bool AppCacheResponseReader::OpenEntryIfNeeded() {
+ if (!entry_)
+ disk_cache_->OpenEntry(response_key(response_id_), &entry_);
+ return entry_ ? true : false;
+}
+
+
+// AppCacheResponseWriter ----------------------------------------------
+
+void AppCacheResponseWriter::WriteInfo(HttpResponseInfoIOBuffer* info_buf,
+ net::CompletionCallback* callback) {
+ DCHECK(callback && !IsWritePending());
+ DCHECK(info_buf && info_buf->http_info.get());
+ DCHECK(!buffer_.get() && !info_buffer_.get());
+
+ user_callback_ = callback; // cleared on completion
+
+ if (!CreateEntryIfNeeded()) {
+ ScheduleIOCompletionCallback(net::ERR_FAILED);
+ return;
+ }
+
+ const bool kSkipTransientHeaders = true;
+ const bool kTruncated = false;
+ Pickle* pickle = new Pickle;
+ info_buf->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated);
+ write_amount_ = static_cast<int>(pickle->size());
+ buffer_ = new WrappedPickleIOBuffer(pickle); // takes ownership of pickle
+ WriteRaw(kResponseInfoIndex, 0, buffer_, write_amount_);
+}
+
+void AppCacheResponseWriter::WriteData(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback) {
+ DCHECK(callback && !IsWritePending());
+ DCHECK(buf && (buf_len >= 0));
+ DCHECK(!buffer_.get() && !info_buffer_.get());
+
+ user_callback_ = callback; // cleared on completion
+
+ if (!CreateEntryIfNeeded()) {
+ ScheduleIOCompletionCallback(net::ERR_FAILED);
+ return;
+ }
+
+ buffer_ = buf;
+ write_amount_ = buf_len;
+ WriteRaw(kResponseContentIndex, write_position_, buf, buf_len);
+}
+
+void AppCacheResponseWriter::OnIOComplete(int result) {
+ if (result >= 0) {
+ DCHECK(write_amount_ == result);
+ if (!info_buffer_.get())
+ write_position_ += result;
+ }
+ InvokeUserCompletionCallback(result);
+}
+
+bool AppCacheResponseWriter::CreateEntryIfNeeded() {
+ if (!entry_)
+ disk_cache_->CreateEntry(response_key(response_id_), &entry_);
+ return entry_ ? true : false;
+}
+
+} // namespace appcache
+