summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authorrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-02 19:10:07 +0000
committerrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-02 19:10:07 +0000
commit47b950579d39dc79127c1dc69f44c33c8ad269b0 (patch)
treea12052d4046260987847c3577b90912915431dcc /net/http
parent7e5a07433628b7383ed7402bc18bcc6676d8475f (diff)
downloadchromium_src-47b950579d39dc79127c1dc69f44c33c8ad269b0.zip
chromium_src-47b950579d39dc79127c1dc69f44c33c8ad269b0.tar.gz
chromium_src-47b950579d39dc79127c1dc69f44c33c8ad269b0.tar.bz2
Http cache: Expose storing metadata on a given entry.
BUG=32406 TEST=unittests Review URL: http://codereview.chromium.org/660041 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40400 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r--net/http/http_cache.cc88
-rw-r--r--net/http/http_cache.h1
-rw-r--r--net/http/http_cache_transaction.cc65
-rw-r--r--net/http/http_cache_transaction.h12
-rw-r--r--net/http/http_cache_unittest.cc137
5 files changed, 283 insertions, 20 deletions
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index cf95c10..32e8e55 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -19,6 +19,7 @@
#include "base/ref_counted.h"
#include "base/string_util.h"
#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache_transaction.h"
@@ -124,6 +125,82 @@ class HttpCache::BackendCallback : public CallbackRunner<Tuple1<int> > {
//-----------------------------------------------------------------------------
+// This class encapsulates a transaction whose only purpose is to write metadata
+// to a given entry.
+class HttpCache::MetadataWriter {
+ public:
+ explicit MetadataWriter(HttpCache::Transaction* trans)
+ : transaction_(trans),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ callback_(this, &MetadataWriter::OnIOComplete)) {}
+ ~MetadataWriter() {}
+
+ // Implementes the bulk of HttpCache::WriteMetadata.
+ void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
+ int buf_len);
+
+ private:
+ void VerifyResponse(int result);
+ void SelfDestroy();
+ void OnIOComplete(int result);
+
+ scoped_ptr<HttpCache::Transaction> transaction_;
+ bool verified_;
+ scoped_refptr<IOBuffer> buf_;
+ int buf_len_;
+ base::Time expected_response_time_;
+ CompletionCallbackImpl<MetadataWriter> callback_;
+ HttpRequestInfo request_info_;
+ DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
+};
+
+void HttpCache::MetadataWriter::Write(const GURL& url,
+ base::Time expected_response_time,
+ IOBuffer* buf, int buf_len) {
+ DCHECK_GT(buf_len, 0);
+ DCHECK(buf);
+ DCHECK(buf->data());
+ request_info_.url = url;
+ request_info_.method = "GET";
+ request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
+
+ expected_response_time_ = expected_response_time;
+ buf_ = buf;
+ buf_len_ = buf_len;
+ verified_ = false;
+
+ int rv = transaction_->Start(&request_info_, &callback_, NULL);
+ if (rv != ERR_IO_PENDING)
+ VerifyResponse(rv);
+}
+
+void HttpCache::MetadataWriter::VerifyResponse(int result) {
+ verified_ = true;
+ if (result != OK)
+ return SelfDestroy();
+
+ const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
+ DCHECK(response_info->was_cached);
+ if (response_info->response_time != expected_response_time_)
+ return SelfDestroy();
+
+ result = transaction_->WriteMetadata(buf_, buf_len_, &callback_);
+ if (result != ERR_IO_PENDING)
+ SelfDestroy();
+}
+
+void HttpCache::MetadataWriter::SelfDestroy() {
+ delete this;
+}
+
+void HttpCache::MetadataWriter::OnIOComplete(int result) {
+ if (!verified_)
+ return VerifyResponse(result);
+ SelfDestroy();
+}
+
+//-----------------------------------------------------------------------------
+
HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
HostResolver* host_resolver,
ProxyService* proxy_service,
@@ -284,7 +361,16 @@ bool HttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry,
void HttpCache::WriteMetadata(const GURL& url,
base::Time expected_response_time, IOBuffer* buf,
int buf_len) {
- // TODO(rvargas): Implement me.
+ if (!buf_len)
+ return;
+
+ GetBackend();
+ HttpCache::Transaction* trans =
+ new HttpCache::Transaction(this, enable_range_support_);
+ MetadataWriter* writer = new MetadataWriter(trans);
+
+ // The writer will self destruct when done.
+ writer->Write(url, expected_response_time, buf, buf_len);
}
// Generate a key that can be used inside the cache.
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index 4cce67b..f9c1d7f 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -163,6 +163,7 @@ class HttpCache : public HttpTransactionFactory,
// Types --------------------------------------------------------------------
class BackendCallback;
+ class MetadataWriter;
class Transaction;
class WorkItem;
friend class Transaction;
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 528a6b7..5b44a76 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -33,7 +33,8 @@ namespace net {
// disk cache entry data indices.
enum {
kResponseInfoIndex,
- kResponseContentIndex
+ kResponseContentIndex,
+ kMetadataIndex
};
//-----------------------------------------------------------------------------
@@ -381,14 +382,20 @@ uint64 HttpCache::Transaction::GetUploadProgress() const {
return final_upload_progress_;
}
-int HttpCache::Transaction::ReadMetadata(IOBuffer* buf, int buf_len,
- CompletionCallback* callback) {
- return ERR_NOT_IMPLEMENTED;
-}
-
int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len,
CompletionCallback* callback) {
- return ERR_NOT_IMPLEMENTED;
+ DCHECK(buf);
+ DCHECK_GT(buf_len, 0);
+ DCHECK(callback);
+ if (!cache_ || !entry_)
+ return ERR_UNEXPECTED;
+
+ // We don't need to track this operation for anything.
+ // It could be possible to check if there is something already written and
+ // avoid writing again (it should be the same, right?), but let's allow the
+ // caller to "update" the contents with something new.
+ return entry_->disk_entry->WriteData(kMetadataIndex, 0, buf, buf_len,
+ callback, true);
}
int HttpCache::Transaction::AddToEntry() {
@@ -731,6 +738,13 @@ int HttpCache::Transaction::DoLoop(int result) {
case STATE_CACHE_WRITE_RESPONSE_COMPLETE:
rv = DoCacheWriteResponseComplete(rv);
break;
+ case STATE_CACHE_READ_METADATA:
+ DCHECK_EQ(OK, rv);
+ rv = DoCacheReadMetadata();
+ break;
+ case STATE_CACHE_READ_METADATA_COMPLETE:
+ rv = DoCacheReadMetadataComplete(rv);
+ break;
case STATE_CACHE_QUERY_DATA:
DCHECK_EQ(OK, rv);
rv = DoCacheQueryData();
@@ -917,6 +931,9 @@ int HttpCache::Transaction::BeginCacheRead() {
if (truncated_)
return ERR_CACHE_MISS;
+ if (entry_->disk_entry->GetDataSize(kMetadataIndex))
+ next_state_ = STATE_CACHE_READ_METADATA;
+
return OK;
}
@@ -927,6 +944,8 @@ int HttpCache::Transaction::BeginCacheValidation() {
!RequiresValidation()) && !partial_.get()) {
cache_->ConvertWriterToReader(entry_);
mode_ = READ;
+ if (entry_ && entry_->disk_entry->GetDataSize(kMetadataIndex))
+ next_state_ = STATE_CACHE_READ_METADATA;
} else {
// Make the network request conditional, to see if we may reuse our cached
// response. If we cannot do so, then we just resort to a normal fetch.
@@ -1452,6 +1471,32 @@ int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) {
return OK;
}
+int HttpCache::Transaction::DoCacheReadMetadata() {
+ DCHECK(entry_);
+ DCHECK(!response_.metadata);
+ next_state_ = STATE_CACHE_READ_METADATA_COMPLETE;
+
+ response_.metadata =
+ new IOBufferWithSize(entry_->disk_entry->GetDataSize(kMetadataIndex));
+
+ LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_READ_INFO);
+ cache_callback_->AddRef(); // Balanced in DoCacheReadMetadataComplete.
+ return entry_->disk_entry->ReadData(kMetadataIndex, 0, response_.metadata,
+ response_.metadata->size(),
+ cache_callback_);
+}
+
+int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) {
+ cache_callback_->Release(); // Balance the AddRef from DoCacheReadMetadata.
+ LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_READ_INFO);
+ if (result != response_.metadata->size()) {
+ DLOG(ERROR) << "ReadData failed: " << result;
+ return ERR_CACHE_READ_FAILURE;
+ }
+
+ return OK;
+}
+
int HttpCache::Transaction::AppendResponseDataToEntry(
IOBuffer* data, int data_len, CompletionCallback* callback) {
if (!entry_ || !data_len)
@@ -1470,6 +1515,8 @@ int HttpCache::Transaction::DoTruncateCachedData() {
// Truncate the stream.
int rv = WriteToEntry(kResponseContentIndex, 0, NULL, 0, NULL);
DCHECK(rv != ERR_IO_PENDING);
+ rv = WriteToEntry(kMetadataIndex, 0, NULL, 0, NULL);
+ DCHECK(rv != ERR_IO_PENDING);
return OK;
}
@@ -1694,6 +1741,10 @@ int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) {
int HttpCache::Transaction::DoPartialHeadersReceived() {
new_response_ = NULL;
+ if (entry_ && !partial_.get() &&
+ entry_->disk_entry->GetDataSize(kMetadataIndex))
+ next_state_ = STATE_CACHE_READ_METADATA;
+
if (!partial_.get())
return OK;
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h
index 7ea1ff0..0adb83a 100644
--- a/net/http/http_cache_transaction.h
+++ b/net/http/http_cache_transaction.h
@@ -72,14 +72,6 @@ class HttpCache::Transaction : public HttpTransaction {
const std::string& key() const { return cache_key_; }
- // Reads up to |buf_len| bytes of meta-data into the provided buffer |buf|,
- // from the HTTP cache entry that backs this transaction (if any).
- // Returns the number of bytes actually read, or a net error code. If the
- // operation cannot complete immediately, returns ERR_IO_PENDING, grabs a
- // reference to the buffer (until completion), and notifies the caller using
- // the provided |callback| when the operatiopn finishes.
- int ReadMetadata(IOBuffer* buf, int buf_len, CompletionCallback* callback);
-
// Writes |buf_len| bytes of meta-data from the provided buffer |buf|. to the
// HTTP cache entry that backs this transaction (if any).
// Returns the number of bytes actually written, or a net error code. If the
@@ -143,6 +135,8 @@ class HttpCache::Transaction : public HttpTransaction {
STATE_CACHE_WRITE_RESPONSE,
STATE_CACHE_WRITE_TRUNCATED_RESPONSE,
STATE_CACHE_WRITE_RESPONSE_COMPLETE,
+ STATE_CACHE_READ_METADATA,
+ STATE_CACHE_READ_METADATA_COMPLETE,
STATE_CACHE_QUERY_DATA,
STATE_CACHE_QUERY_DATA_COMPLETE,
STATE_CACHE_READ_DATA,
@@ -190,6 +184,8 @@ class HttpCache::Transaction : public HttpTransaction {
int DoCacheWriteResponse();
int DoCacheWriteTruncatedResponse();
int DoCacheWriteResponseComplete(int result);
+ int DoCacheReadMetadata();
+ int DoCacheReadMetadataComplete(int result);
int DoCacheQueryData();
int DoCacheQueryDataComplete(int result);
int DoCacheReadData();
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 663176c..5ad2e40 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -101,13 +101,13 @@ class MockDiskEntry : public disk_cache::Entry,
}
virtual int32 GetDataSize(int index) const {
- DCHECK(index >= 0 && index < 2);
+ DCHECK(index >= 0 && index < 3);
return static_cast<int32>(data_[index].size());
}
virtual int ReadData(int index, int offset, net::IOBuffer* buf, int buf_len,
net::CompletionCallback* callback) {
- DCHECK(index >= 0 && index < 2);
+ DCHECK(index >= 0 && index < 3);
if (fail_requests_)
return net::ERR_CACHE_READ_FAILURE;
@@ -130,7 +130,7 @@ class MockDiskEntry : public disk_cache::Entry,
virtual int WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
net::CompletionCallback* callback, bool truncate) {
- DCHECK(index >= 0 && index < 2);
+ DCHECK(index >= 0 && index < 3);
DCHECK(truncate);
if (fail_requests_)
@@ -338,7 +338,7 @@ class MockDiskEntry : public disk_cache::Entry,
}
std::string key_;
- std::vector<char> data_[2];
+ std::vector<char> data_[3];
int test_mode_;
bool doomed_;
bool sparse_;
@@ -4220,3 +4220,132 @@ TEST(HttpCache, UpdatesRequestResponseTimeOn304) {
RemoveMockTransaction(&mock_network_response);
}
+
+// Tests that we can write metadata to an entry.
+TEST(HttpCache, WriteMetadata_OK) {
+ MockHttpCache cache;
+
+ // Write to the cache
+ net::HttpResponseInfo response;
+ RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+ &response);
+ EXPECT_TRUE(response.metadata.get() == NULL);
+
+ // Trivial call.
+ cache.http_cache()->WriteMetadata(GURL("foo"), Time::Now(), NULL, 0);
+
+ // Write meta data to the same entry.
+ scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(50));
+ memset(buf->data(), 0, buf->size());
+ base::strlcpy(buf->data(), "Hi there", buf->size());
+ cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
+ response.response_time, buf, buf->size());
+
+ // Release the buffer before the operation takes place.
+ buf = NULL;
+
+ // Makes sure we finish pending operations.
+ MessageLoop::current()->RunAllPending();
+
+ RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+ &response);
+ ASSERT_TRUE(response.metadata.get() != NULL);
+ EXPECT_EQ(50, response.metadata->size());
+ EXPECT_EQ(0, strcmp(response.metadata->data(), "Hi there"));
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(2, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that we only write metadata to an entry if the time stamp matches.
+TEST(HttpCache, WriteMetadata_Fail) {
+ MockHttpCache cache;
+
+ // Write to the cache
+ net::HttpResponseInfo response;
+ RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+ &response);
+ EXPECT_TRUE(response.metadata.get() == NULL);
+
+ // Attempt to write meta data to the same entry.
+ scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(50));
+ memset(buf->data(), 0, buf->size());
+ base::strlcpy(buf->data(), "Hi there", buf->size());
+ base::Time expected_time = response.response_time -
+ base::TimeDelta::FromMilliseconds(20);
+ cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
+ expected_time, buf, buf->size());
+
+ // Makes sure we finish pending operations.
+ MessageLoop::current()->RunAllPending();
+
+ RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+ &response);
+ EXPECT_TRUE(response.metadata.get() == NULL);
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(2, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that we can read metadata after validating the entry and with READ mode
+// transactions.
+TEST(HttpCache, ReadMetadata) {
+ MockHttpCache cache;
+
+ // Write to the cache
+ net::HttpResponseInfo response;
+ RunTransactionTestWithResponseInfo(cache.http_cache(),
+ kTypicalGET_Transaction, &response);
+ EXPECT_TRUE(response.metadata.get() == NULL);
+
+ // Write meta data to the same entry.
+ scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(50));
+ memset(buf->data(), 0, buf->size());
+ base::strlcpy(buf->data(), "Hi there", buf->size());
+ cache.http_cache()->WriteMetadata(GURL(kTypicalGET_Transaction.url),
+ response.response_time, buf, buf->size());
+
+ // Makes sure we finish pending operations.
+ MessageLoop::current()->RunAllPending();
+
+ // Start with a READ mode transaction.
+ MockTransaction trans1(kTypicalGET_Transaction);
+ trans1.load_flags = net::LOAD_ONLY_FROM_CACHE;
+
+ RunTransactionTestWithResponseInfo(cache.http_cache(), trans1, &response);
+ ASSERT_TRUE(response.metadata.get() != NULL);
+ EXPECT_EQ(50, response.metadata->size());
+ EXPECT_EQ(0, strcmp(response.metadata->data(), "Hi there"));
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(2, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ MessageLoop::current()->RunAllPending();
+
+ // Now make sure that the entry is re-validated with the server.
+ trans1.load_flags = net::LOAD_VALIDATE_CACHE;
+ trans1.status = "HTTP/1.1 304 Not Modified";
+ AddMockTransaction(&trans1);
+
+ response.metadata = NULL;
+ RunTransactionTestWithResponseInfo(cache.http_cache(), trans1, &response);
+ EXPECT_TRUE(response.metadata.get() != NULL);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(3, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ MessageLoop::current()->RunAllPending();
+ RemoveMockTransaction(&trans1);
+
+ // Now return 200 when validating the entry so the metadata will be lost.
+ MockTransaction trans2(kTypicalGET_Transaction);
+ trans2.load_flags = net::LOAD_VALIDATE_CACHE;
+ RunTransactionTestWithResponseInfo(cache.http_cache(), trans2, &response);
+ EXPECT_TRUE(response.metadata.get() == NULL);
+
+ EXPECT_EQ(3, cache.network_layer()->transaction_count());
+ EXPECT_EQ(4, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}