diff options
Diffstat (limited to 'net/http')
-rw-r--r-- | net/http/http_cache.cc | 88 | ||||
-rw-r--r-- | net/http/http_cache.h | 1 | ||||
-rw-r--r-- | net/http/http_cache_transaction.cc | 65 | ||||
-rw-r--r-- | net/http/http_cache_transaction.h | 12 | ||||
-rw-r--r-- | net/http/http_cache_unittest.cc | 137 |
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()); +} |