diff options
author | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-21 18:09:33 +0000 |
---|---|---|
committer | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-21 18:09:33 +0000 |
commit | 073aabe9e48306216d979f03d6c2fc37d25d42e9 (patch) | |
tree | a9088a3c8be87fa57208074c7de65e998b865f94 /webkit/appcache/appcache_response.cc | |
parent | c54d269e0b8a057533c1a1455a2acea0659629b9 (diff) | |
download | chromium_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.cc | 262 |
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 + |