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

Unified Diff: net/http/http_cache.cc

Issue 118345: Http Cache: First pass of byte-range requests support.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 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
« no previous file with comments | « no previous file | net/http/http_cache_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/http/http_cache.cc
===================================================================
--- net/http/http_cache.cc (revision 18197)
+++ net/http/http_cache.cc (working copy)
@@ -28,9 +28,13 @@
#include "net/http/http_response_info.h"
#include "net/http/http_transaction.h"
#include "net/http/http_util.h"
+#include "net/http/partial_data.h"
using base::Time;
+// Uncomment this to enable experimental byte-range support.
+// #define ENABLE_RANGE_SUPPORT
+
namespace net {
// disk cache entry data indices.
@@ -76,7 +80,6 @@
// If the request includes one of these request headers, then avoid caching
// to avoid getting confused.
static const HeaderNameAndValue kPassThroughHeaders[] = {
- { "range", NULL }, // causes unexpected 206s
{ "if-modified-since", NULL }, // causes unexpected 304s
{ "if-none-match", NULL }, // causes unexpected 304s
{ "if-unmodified-since", NULL }, // causes unexpected 412s
@@ -119,8 +122,6 @@
//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-
HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* e)
: disk_entry(e),
writer(NULL),
@@ -146,6 +147,7 @@
network_trans_(NULL),
callback_(NULL),
mode_(NONE),
+ reading_(false),
read_offset_(0),
effective_load_flags_(0),
final_upload_progress_(0),
@@ -231,6 +233,15 @@
// Called to begin validating the cache entry. Returns network error code.
int BeginCacheValidation();
+ // Called to begin validating an entry that stores partial content. Returns
+ // a network error code.
+ int BeginPartialCacheValidation();
+
+ // Performs the cache validation for the next chunk of data stored by the
+ // cache. If this chunk is not currently stored, starts the network request
+ // to fetch it. Returns a network error code.
+ int ContinuePartialCacheValidation();
+
// Called to begin a network transaction. Returns network error code.
int BeginNetworkRequest();
@@ -250,6 +261,12 @@
// copy is valid). Returns true if able to make the request conditional.
bool ConditionalizeRequest();
+ // Reads data from the network.
+ int ReadFromNetwork(IOBuffer* data, int data_len);
+
+ // Reads data from the cache entry.
+ int ReadFromEntry(IOBuffer* data, int data_len);
+
// Called to populate response_ from the cache entry.
int ReadResponseInfoFromEntry();
@@ -270,6 +287,20 @@
// Called when we are done writing to the cache entry.
void DoneWritingToEntry(bool success);
+ // Performs the needed work after receiving data from the network.
+ int DoNetworkReadCompleted(int result);
+
+ // Performs the needed work after receiving data from the network, when
+ // working with range requests.
+ int DoPartialNetworkReadCompleted(int result);
+
+ // Performs the needed work after receiving data from the cache.
+ int DoCacheReadCompleted(int result);
+
+ // Performs the needed work after receiving data from the cache, when
+ // working with range requests.
+ int DoPartialCacheReadCompleted(int result);
+
// Called to signal completion of the network transaction's Start method:
void OnNetworkInfoAvailable(int result);
@@ -284,14 +315,17 @@
HttpCache* cache_;
HttpCache::ActiveEntry* entry_;
scoped_ptr<HttpTransaction> network_trans_;
- CompletionCallback* callback_; // consumer's callback
+ CompletionCallback* callback_; // Consumer's callback.
HttpResponseInfo response_;
HttpResponseInfo auth_response_;
std::string cache_key_;
Mode mode_;
+ bool reading_; // We are already reading.
scoped_refptr<IOBuffer> read_buf_;
+ int read_buf_len_;
int read_offset_;
int effective_load_flags_;
+ scoped_ptr<PartialData> partial_; // We are dealing with range requests.
uint64 final_upload_progress_;
CompletionCallbackImpl<Transaction> network_info_callback_;
CompletionCallbackImpl<Transaction> network_read_callback_;
@@ -433,33 +467,31 @@
int rv;
switch (mode_) {
+ case READ_WRITE:
+ DCHECK(partial_.get());
+ reading_ = true;
+ if (!network_trans_.get()) {
+ // We are just reading from the cache, but we may be writing later.
+ rv = ReadFromEntry(buf, buf_len);
+ break;
+ }
case NONE:
case WRITE:
DCHECK(network_trans_.get());
- rv = network_trans_->Read(buf, buf_len, &network_read_callback_);
- read_buf_ = buf;
- if (rv >= 0)
- OnNetworkReadCompleted(rv);
+ rv = ReadFromNetwork(buf, buf_len);
break;
case READ:
- DCHECK(entry_);
- cache_read_callback_->AddRef(); // Balanced in OnCacheReadCompleted.
- rv = entry_->disk_entry->ReadData(kResponseContentIndex, read_offset_,
- buf, buf_len, cache_read_callback_);
- read_buf_ = buf;
- if (rv >= 0) {
- OnCacheReadCompleted(rv);
- } else if (rv != ERR_IO_PENDING) {
- cache_read_callback_->Release();
- }
+ rv = ReadFromEntry(buf, buf_len);
break;
default:
NOTREACHED();
rv = ERR_FAILED;
}
- if (rv == ERR_IO_PENDING)
+ if (rv == ERR_IO_PENDING) {
+ DCHECK(!callback_);
callback_ = callback;
+ }
return rv;
}
@@ -544,10 +576,12 @@
rv = BeginCacheRead();
break;
case WRITE:
+ if (partial_.get())
+ partial_->RestoreHeaders(&custom_request_->extra_headers);
rv = BeginNetworkRequest();
break;
case READ_WRITE:
- rv = BeginCacheValidation();
+ rv = BeginPartialCacheValidation();
break;
default:
NOTREACHED();
@@ -577,7 +611,7 @@
request_ = request;
effective_load_flags_ = request_->load_flags;
- switch(cache_->mode()) {
+ switch (cache_->mode()) {
case NORMAL:
break;
case RECORD:
@@ -613,11 +647,25 @@
{ kForceValidateHeaders, LOAD_VALIDATE_CACHE },
};
+ std::string new_extra_headers;
+ bool range_found = false;
+
// scan request headers to see if we have any that would impact our load flags
HttpUtil::HeadersIterator it(request_->extra_headers.begin(),
request_->extra_headers.end(),
"\r\n");
while (it.GetNext()) {
+ if (!LowerCaseEqualsASCII(it.name(), "range")) {
+ new_extra_headers.append(it.name_begin(), it.values_end());
+ new_extra_headers.append("\r\n");
+ } else {
+#ifdef ENABLE_RANGE_SUPPORT
+ range_found = true;
+#else
+ effective_load_flags_ |= LOAD_DISABLE_CACHE;
+ continue;
+#endif
+ }
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSpecialHeaders); ++i) {
if (HeaderMatches(it, kSpecialHeaders[i].search)) {
effective_load_flags_ |= kSpecialHeaders[i].load_flag;
@@ -625,6 +673,20 @@
}
}
}
+
+ if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) {
+ partial_.reset(new PartialData);
+ if (partial_->Init(request_->extra_headers, new_extra_headers)) {
+ // We will be modifying the actual range requested to the server, so
+ // let's remove the header here.
+ custom_request_.reset(new HttpRequestInfo(*request_));
+ request_ = custom_request_.get();
+ custom_request_->extra_headers = new_extra_headers;
+ } else {
+ // The range is invalid or we cannot handle it properly.
+ effective_load_flags_ |= LOAD_DISABLE_CACHE;
+ }
+ }
}
bool HttpCache::Transaction::ShouldPassThrough() {
@@ -655,18 +717,16 @@
int HttpCache::Transaction::BeginCacheRead() {
DCHECK(mode_ == READ);
- // read response headers
+ // Read response headers.
+ // TODO(rvargas): Handle partial content (206).
return HandleResult(ReadResponseInfoFromEntry());
}
int HttpCache::Transaction::BeginCacheValidation() {
DCHECK(mode_ == READ_WRITE);
- int rv = ReadResponseInfoFromEntry();
- if (rv != OK) {
- DCHECK(rv != ERR_IO_PENDING);
- } else if (effective_load_flags_ & LOAD_PREFERRING_CACHE ||
- !RequiresValidation()) {
+ if ((effective_load_flags_ & LOAD_PREFERRING_CACHE ||
+ !RequiresValidation()) && !partial_.get()) {
cache_->ConvertWriterToReader(entry_);
mode_ = READ;
} else {
@@ -678,9 +738,49 @@
mode_ = WRITE;
return BeginNetworkRequest();
}
- return HandleResult(rv);
+ return HandleResult(OK);
}
+int HttpCache::Transaction::BeginPartialCacheValidation() {
+ DCHECK(mode_ == READ_WRITE);
+
+ int rv = ReadResponseInfoFromEntry();
+ if (rv != OK) {
+ DCHECK(rv != ERR_IO_PENDING);
+ return HandleResult(rv);
+ }
+
+ if (response_.headers->response_code() != 206)
+ return BeginCacheValidation();
+
+ if (!partial_.get()) {
+ // The request is not for a range, but we have stored just ranges.
+ // TODO(rvargas): Add support for this case.
+ NOTREACHED();
+ }
+
+ return ContinuePartialCacheValidation();
+}
+
+int HttpCache::Transaction::ContinuePartialCacheValidation() {
+ DCHECK(mode_ == READ_WRITE);
+
+ int rv = partial_->PrepareCacheValidation(entry_->disk_entry,
+ &custom_request_->extra_headers);
+
+ if (!rv) {
+ // Don't invoke the callback before telling the cache we're done.
+ return rv;
+ }
+
+ if (rv < 0) {
+ DCHECK(rv != ERR_IO_PENDING);
+ return HandleResult(rv);
+ }
+
+ return BeginCacheValidation();
+}
+
int HttpCache::Transaction::BeginNetworkRequest() {
DCHECK(mode_ & WRITE || mode_ == NONE);
DCHECK(!network_trans_.get());
@@ -744,8 +844,9 @@
bool HttpCache::Transaction::ConditionalizeRequest() {
DCHECK(response_.headers);
- // This only makes sense for cached 200 responses.
- if (response_.headers->response_code() != 200)
+ // This only makes sense for cached 200 or 206 responses.
+ if (response_.headers->response_code() != 200 &&
+ response_.headers->response_code() != 206)
return false;
// Just use the first available ETag and/or Last-Modified header value.
@@ -761,18 +862,33 @@
if (etag_value.empty() && last_modified_value.empty())
return false;
- // Need to customize the request, so this forces us to allocate :(
- custom_request_.reset(new HttpRequestInfo(*request_));
- request_ = custom_request_.get();
+ if (!partial_.get()) {
+ // Need to customize the request, so this forces us to allocate :(
+ custom_request_.reset(new HttpRequestInfo(*request_));
+ request_ = custom_request_.get();
+ }
+ DCHECK(custom_request_.get());
if (!etag_value.empty()) {
- custom_request_->extra_headers.append("If-None-Match: ");
+ if (partial_.get() && !partial_->IsCurrentRangeCached()) {
+ // We don't want to switch to WRITE mode if we don't have this block of a
+ // byte-range request because we may have other parts cached.
+ custom_request_->extra_headers.append("If-Range: ");
+ } else {
+ custom_request_->extra_headers.append("If-None-Match: ");
+ }
custom_request_->extra_headers.append(etag_value);
custom_request_->extra_headers.append("\r\n");
+ if (partial_.get() && !partial_->IsCurrentRangeCached())
+ return true;
}
if (!last_modified_value.empty()) {
- custom_request_->extra_headers.append("If-Modified-Since: ");
+ if (partial_.get() && !partial_->IsCurrentRangeCached()) {
+ custom_request_->extra_headers.append("If-Range: ");
+ } else {
+ custom_request_->extra_headers.append("If-Modified-Since: ");
+ }
custom_request_->extra_headers.append(last_modified_value);
custom_request_->extra_headers.append("\r\n");
}
@@ -780,6 +896,36 @@
return true;
}
+int HttpCache::Transaction::ReadFromNetwork(IOBuffer* data, int data_len) {
+ int rv = network_trans_->Read(data, data_len, &network_read_callback_);
+ read_buf_ = data;
+ read_buf_len_ = data_len;
+ if (rv >= 0)
+ rv = DoNetworkReadCompleted(rv);
+ return rv;
+}
+
+int HttpCache::Transaction::ReadFromEntry(IOBuffer* data, int data_len) {
+ DCHECK(entry_);
+ int rv;
+ cache_read_callback_->AddRef(); // Balanced in DoCacheReadCompleted.
+ if (partial_.get()) {
+ rv = partial_->CacheRead(entry_->disk_entry, data, data_len,
+ cache_read_callback_);
+ } else {
+ rv = entry_->disk_entry->ReadData(kResponseContentIndex, read_offset_,
+ data, data_len, cache_read_callback_);
+ }
+ read_buf_ = data;
+ read_buf_len_ = data_len;
+ if (rv >= 0) {
+ rv = DoCacheReadCompleted(rv);
+ } else if (rv != ERR_IO_PENDING) {
+ cache_read_callback_->Release();
+ }
+ return rv;
+}
+
int HttpCache::Transaction::ReadResponseInfoFromEntry() {
DCHECK(entry_);
@@ -793,8 +939,13 @@
if (!entry_)
return;
- int rv = entry_->disk_entry->WriteData(index, offset, data, data_len, NULL,
- true);
+ int rv = 0;
+ if (!partial_.get() || !data_len) {
+ rv = entry_->disk_entry->WriteData(index, offset, data, data_len, NULL,
+ true);
+ } else {
+ rv = partial_->CacheWrite(entry_->disk_entry, data, data_len, NULL);
+ }
if (rv != data_len) {
DLOG(ERROR) << "failed to write response data to cache";
DoneWritingToEntry(false);
@@ -834,7 +985,7 @@
void HttpCache::Transaction::AppendResponseDataToEntry(IOBuffer* data,
int data_len) {
- if (!entry_)
+ if (!entry_ || !data_len)
return;
int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
@@ -876,10 +1027,19 @@
new_response->headers->response_code() == 407) {
auth_response_ = *new_response;
} else {
+#ifdef ENABLE_RANGE_SUPPORT
+ bool partial_content = new_response->headers->response_code() == 206;
+#else
+ bool partial_content = false;
+#endif
+ // TODO(rvargas): Validate partial_content vs partial_ and mode_
+ if (partial_content) {
+ DCHECK(partial_.get());
+ }
// Are we expecting a response to a conditional query?
if (mode_ == READ_WRITE) {
- if (new_response->headers->response_code() == 304) {
- // Update cached response based on headers in new_response
+ if (new_response->headers->response_code() == 304 || partial_content) {
+ // Update cached response based on headers in new_response.
// TODO(wtc): should we update cached certificate
// (response_.ssl_info), too?
response_.headers->Update(*new_response->headers);
@@ -889,12 +1049,14 @@
WriteResponseInfoToEntry();
}
- if (entry_) {
- cache_->ConvertWriterToReader(entry_);
+ if (entry_ && !partial_content) {
+ if (!partial_.get() || partial_->IsLastRange())
+ cache_->ConvertWriterToReader(entry_);
// We no longer need the network transaction, so destroy it.
final_upload_progress_ = network_trans_->GetUploadProgress();
network_trans_.reset();
- mode_ = READ;
+ if (!partial_.get() || partial_->IsLastRange())
+ mode_ = READ;
}
} else {
mode_ = WRITE;
@@ -905,7 +1067,7 @@
response_ = *new_response;
WriteResponseInfoToEntry();
- // Truncate response data
+ // Truncate response data.
TruncateResponseData();
// If this response is a redirect, then we can stop writing now. (We
@@ -913,6 +1075,16 @@
if (response_.headers->IsRedirect(NULL))
DoneWritingToEntry(true);
}
+ if (reading_) {
+ DCHECK(partial_.get());
+ if (network_trans_.get()) {
+ result = ReadFromNetwork(read_buf_, read_buf_len_);
+ } else {
+ result = ReadFromEntry(read_buf_, read_buf_len_);
+ }
+ if (result >= 0 || result == net::ERR_IO_PENDING)
+ return;
+ }
}
} else if (IsCertificateError(result)) {
const HttpResponseInfo* response = network_trans_->GetResponseInfo();
@@ -925,39 +1097,84 @@
}
void HttpCache::Transaction::OnNetworkReadCompleted(int result) {
+ DoNetworkReadCompleted(result);
+}
+
+int HttpCache::Transaction::DoNetworkReadCompleted(int result) {
DCHECK(mode_ & WRITE || mode_ == NONE);
- if (revoked()) {
- HandleResult(ERR_UNEXPECTED);
- return;
- }
+ if (revoked())
+ return HandleResult(ERR_UNEXPECTED);
- if (result > 0) {
- AppendResponseDataToEntry(read_buf_, result);
- } else if (result == 0) { // end of file
+ AppendResponseDataToEntry(read_buf_, result);
+
+ if (partial_.get())
+ return DoPartialNetworkReadCompleted(result);
+
+ if (result == 0) // End of file.
DoneWritingToEntry(true);
+
+ return HandleResult(result);
+}
+
+int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) {
+ partial_->OnNetworkReadCompleted(result);
+
+ if (result == 0) { // End of file.
+ if (mode_ == READ_WRITE) {
+ // We need to move on to the next range.
+ network_trans_.reset();
+ result = ContinuePartialCacheValidation();
+ if (result != OK)
+ // Any error was already handled.
+ return result;
+ }
+ DoneWritingToEntry(true);
}
- HandleResult(result);
+ return HandleResult(result);
}
void HttpCache::Transaction::OnCacheReadCompleted(int result) {
+ DoCacheReadCompleted(result);
+}
+
+int HttpCache::Transaction::DoCacheReadCompleted(int result) {
DCHECK(cache_);
cache_read_callback_->Release(); // Balance the AddRef() from Start().
- if (revoked()) {
- HandleResult(ERR_UNEXPECTED);
- return;
- }
+ if (revoked())
+ return HandleResult(ERR_UNEXPECTED);
+ if (partial_.get())
+ return DoPartialCacheReadCompleted(result);
+
if (result > 0) {
read_offset_ += result;
- } else if (result == 0) { // end of file
+ } else if (result == 0) { // End of file.
cache_->DoneReadingFromEntry(entry_, this);
entry_ = NULL;
}
- HandleResult(result);
+ return HandleResult(result);
}
+int HttpCache::Transaction::DoPartialCacheReadCompleted(int result) {
+ partial_->OnCacheReadCompleted(result);
+
+ if (result == 0) { // End of file.
+ if (partial_.get() && mode_ == READ_WRITE) {
+ // We need to move on to the next range.
+ result = ContinuePartialCacheValidation();
+ if (result != OK)
+ // Any error was already handled.
+ return result;
+ cache_->ConvertWriterToReader(entry_);
+ }
+ cache_->DoneReadingFromEntry(entry_, this);
+ entry_ = NULL;
+ }
+ return HandleResult(result);
+}
+
//-----------------------------------------------------------------------------
HttpCache::HttpCache(ProxyService* proxy_service,
@@ -1144,7 +1361,8 @@
net::HttpResponseHeaders::PERSIST_SANS_COOKIES |
net::HttpResponseHeaders::PERSIST_SANS_CHALLENGES |
net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP |
- net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE;
+ net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
+ net::HttpResponseHeaders::PERSIST_SANS_RANGES;
}
response_info->headers->Persist(&pickle, persist_options);
« no previous file with comments | « no previous file | net/http/http_cache_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698