Commit 9f03cb7a authored by rvargas@google.com's avatar rvargas@google.com

Http cache: Make sure that issuing a byte range request

does not prevent caching of responses that are not 200,
and does not delete the cached response if not really needed.

For example, we should be able to cache redirects when the
request is made using a byte range.

BUG=134267
TEST=net_unittests
Review URL: https://chromiumcodereview.appspot.com/10829069

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149069 0039d316-1c4b-4281-b951-d872f2087c98
parent 913069ad
...@@ -1611,15 +1611,24 @@ int HttpCache::Transaction::BeginCacheValidation() { ...@@ -1611,15 +1611,24 @@ int HttpCache::Transaction::BeginCacheValidation() {
if (truncated_) if (truncated_)
skip_validation = !partial_->initial_validation(); skip_validation = !partial_->initial_validation();
if ((partial_.get() && !partial_->IsCurrentRangeCached()) || invalid_range_) if (partial_.get() && (is_sparse_ || truncated_) &&
(!partial_->IsCurrentRangeCached() || invalid_range_)) {
// Force revalidation for sparse or truncated entries. Note that we don't
// want to ignore the regular validation logic just because a byte range was
// part of the request.
skip_validation = false; skip_validation = false;
}
if (skip_validation) { if (skip_validation) {
if (partial_.get()) { if (partial_.get()) {
// We are going to return the saved response headers to the caller, so if (truncated_ || is_sparse_ || !invalid_range_) {
// we may need to adjust them first. // We are going to return the saved response headers to the caller, so
next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; // we may need to adjust them first.
return OK; next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
return OK;
} else {
partial_.reset();
}
} }
cache_->ConvertWriterToReader(entry_); cache_->ConvertWriterToReader(entry_);
mode_ = READ; mode_ = READ;
...@@ -1632,7 +1641,9 @@ int HttpCache::Transaction::BeginCacheValidation() { ...@@ -1632,7 +1641,9 @@ int HttpCache::Transaction::BeginCacheValidation() {
// Our mode remains READ_WRITE for a conditional request. We'll switch to // Our mode remains READ_WRITE for a conditional request. We'll switch to
// either READ or WRITE mode once we hear back from the server. // either READ or WRITE mode once we hear back from the server.
if (!ConditionalizeRequest()) { if (!ConditionalizeRequest()) {
DCHECK(!partial_.get()); if (partial_.get())
return DoRestartPartialRequest();
DCHECK_NE(206, response_.headers->response_code()); DCHECK_NE(206, response_.headers->response_code());
mode_ = WRITE; mode_ = WRITE;
} }
...@@ -1669,13 +1680,7 @@ int HttpCache::Transaction::ValidateEntryHeadersAndContinue() { ...@@ -1669,13 +1680,7 @@ int HttpCache::Transaction::ValidateEntryHeadersAndContinue() {
if (!partial_->UpdateFromStoredHeaders(response_.headers, entry_->disk_entry, if (!partial_->UpdateFromStoredHeaders(response_.headers, entry_->disk_entry,
truncated_)) { truncated_)) {
// The stored data cannot be used. Get rid of it and restart this request. return DoRestartPartialRequest();
// We need to also reset the |truncated_| flag as a new entry is created.
DoomPartialEntry(!range_requested_);
mode_ = WRITE;
truncated_ = false;
next_state_ = STATE_INIT_ENTRY;
return OK;
} }
if (response_.headers->response_code() == 206) if (response_.headers->response_code() == 206)
...@@ -1915,12 +1920,19 @@ bool HttpCache::Transaction::ValidatePartialResponse() { ...@@ -1915,12 +1920,19 @@ bool HttpCache::Transaction::ValidatePartialResponse() {
return true; return true;
} }
if (response_code == 200 && !reading_ && !is_sparse_) { if (!reading_ && !is_sparse_ && !partial_response) {
// The server is sending the whole resource, and we can save it. // See if we can ignore the fact that we issued a byte range request.
DCHECK((truncated_ && !partial_->IsLastRange()) || range_requested_); // If the server sends 200, just store it. If it sends an error, redirect
partial_.reset(); // or something else, we may store the response as long as we didn't have
truncated_ = false; // anything already stored.
return true; if (response_code == 200 ||
(!truncated_ && response_code != 304 && response_code != 416)) {
// The server is sending something else, and we can save it.
DCHECK((truncated_ && !partial_->IsLastRange()) || range_requested_);
partial_.reset();
truncated_ = false;
return true;
}
} }
// 304 is not expected here, but we'll spare the entry (unless it was // 304 is not expected here, but we'll spare the entry (unless it was
...@@ -2117,6 +2129,16 @@ int HttpCache::Transaction::DoPartialCacheReadCompleted(int result) { ...@@ -2117,6 +2129,16 @@ int HttpCache::Transaction::DoPartialCacheReadCompleted(int result) {
return result; return result;
} }
int HttpCache::Transaction::DoRestartPartialRequest() {
// The stored data cannot be used. Get rid of it and restart this request.
// We need to also reset the |truncated_| flag as a new entry is created.
DoomPartialEntry(!range_requested_);
mode_ = WRITE;
truncated_ = false;
next_state_ = STATE_INIT_ENTRY;
return OK;
}
// Histogram data from the end of 2010 show the following distribution of // Histogram data from the end of 2010 show the following distribution of
// response headers: // response headers:
// //
......
...@@ -327,6 +327,11 @@ class HttpCache::Transaction : public HttpTransaction { ...@@ -327,6 +327,11 @@ class HttpCache::Transaction : public HttpTransaction {
// working with range requests. // working with range requests.
int DoPartialCacheReadCompleted(int result); int DoPartialCacheReadCompleted(int result);
// Restarts this transaction after deleting the cached data. It is meant to
// be used when the current request cannot be fulfilled due to conflicts
// between the byte range request and the cached entry.
int DoRestartPartialRequest();
// Returns true if we should bother attempting to resume this request if it // Returns true if we should bother attempting to resume this request if it
// is aborted while in progress. If |has_data| is true, the size of the stored // is aborted while in progress. If |has_data| is true, the size of the stored
// data is considered for the result. // data is considered for the result.
......
...@@ -2805,6 +2805,31 @@ TEST(HttpCache, RangeGET_ModifiedResult) { ...@@ -2805,6 +2805,31 @@ TEST(HttpCache, RangeGET_ModifiedResult) {
RemoveMockTransaction(&kRangeGET_TransactionOK); RemoveMockTransaction(&kRangeGET_TransactionOK);
} }
// Tests that we cache 301s for range requests.
TEST(HttpCache, RangeGET_301) {
MockHttpCache cache;
ScopedMockTransaction transaction(kRangeGET_TransactionOK);
transaction.status = "HTTP/1.1 301 Moved Permanently";
transaction.response_headers = "Location: http://www.bar.com/\n";
transaction.data = "";
transaction.handler = NULL;
AddMockTransaction(&transaction);
// Write to the cache.
RunTransactionTest(cache.http_cache(), transaction);
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
EXPECT_EQ(1, cache.disk_cache()->create_count());
// Read from the cache.
RunTransactionTest(cache.http_cache(), transaction);
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(1, cache.disk_cache()->open_count());
EXPECT_EQ(1, cache.disk_cache()->create_count());
RemoveMockTransaction(&transaction);
}
// Tests that we can cache range requests when the start or end is unknown. // Tests that we can cache range requests when the start or end is unknown.
// We start with one suffix request, followed by a request from a given point. // We start with one suffix request, followed by a request from a given point.
TEST(HttpCache, UnknownRangeGET_1) { TEST(HttpCache, UnknownRangeGET_1) {
...@@ -3184,13 +3209,13 @@ TEST(HttpCache, RangeGET_Previous200) { ...@@ -3184,13 +3209,13 @@ TEST(HttpCache, RangeGET_Previous200) {
// Make a request for an invalid range. // Make a request for an invalid range.
MockTransaction transaction3(kRangeGET_TransactionOK); MockTransaction transaction3(kRangeGET_TransactionOK);
transaction3.request_headers = "Range: bytes = 80-90\r\n" EXTRA_HEADER; transaction3.request_headers = "Range: bytes = 80-90\r\n" EXTRA_HEADER;
transaction3.data = ""; transaction3.data = transaction.data;
transaction3.load_flags = net::LOAD_PREFERRING_CACHE; transaction3.load_flags = net::LOAD_PREFERRING_CACHE;
RunTransactionTestWithResponse(cache.http_cache(), transaction3, &headers); RunTransactionTestWithResponse(cache.http_cache(), transaction3, &headers);
EXPECT_EQ(2, cache.disk_cache()->open_count()); EXPECT_EQ(2, cache.disk_cache()->open_count());
EXPECT_EQ(0U, headers.find("HTTP/1.1 416 ")); EXPECT_EQ(0U, headers.find("HTTP/1.1 200 "));
EXPECT_NE(std::string::npos, headers.find("Content-Range: bytes 0-0/80")); EXPECT_EQ(std::string::npos, headers.find("Content-Range:"));
EXPECT_NE(std::string::npos, headers.find("Content-Length: 0")); EXPECT_EQ(std::string::npos, headers.find("Content-Length: 80"));
// Make sure the entry is deactivated. // Make sure the entry is deactivated.
MessageLoop::current()->RunAllPending(); MessageLoop::current()->RunAllPending();
...@@ -3207,7 +3232,7 @@ TEST(HttpCache, RangeGET_Previous200) { ...@@ -3207,7 +3232,7 @@ TEST(HttpCache, RangeGET_Previous200) {
transaction2.request_headers = kRangeGET_TransactionOK.request_headers; transaction2.request_headers = kRangeGET_TransactionOK.request_headers;
RunTransactionTestWithResponse(cache.http_cache(), transaction2, &headers); RunTransactionTestWithResponse(cache.http_cache(), transaction2, &headers);
Verify206Response(headers, 40, 49); Verify206Response(headers, 40, 49);
EXPECT_EQ(5, cache.network_layer()->transaction_count()); EXPECT_EQ(4, cache.network_layer()->transaction_count());
EXPECT_EQ(4, cache.disk_cache()->open_count()); EXPECT_EQ(4, cache.disk_cache()->open_count());
EXPECT_EQ(1, cache.disk_cache()->create_count()); EXPECT_EQ(1, cache.disk_cache()->create_count());
......
...@@ -242,9 +242,6 @@ bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers, ...@@ -242,9 +242,6 @@ bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
disk_cache::Entry* entry, disk_cache::Entry* entry,
bool truncated) { bool truncated) {
resource_size_ = 0; resource_size_ = 0;
if (!headers->HasStrongValidators())
return false;
if (truncated) { if (truncated) {
DCHECK_EQ(headers->response_code(), 200); DCHECK_EQ(headers->response_code(), 200);
// We don't have the real length and the user may be trying to create a // We don't have the real length and the user may be trying to create a
...@@ -252,6 +249,9 @@ bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers, ...@@ -252,6 +249,9 @@ bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
if (byte_range_.IsValid()) if (byte_range_.IsValid())
return false; return false;
if (!headers->HasStrongValidators())
return false;
// Now we avoid resume if there is no content length, but that was not // Now we avoid resume if there is no content length, but that was not
// always the case so double check here. // always the case so double check here.
int64 total_length = headers->GetContentLength(); int64 total_length = headers->GetContentLength();
...@@ -270,7 +270,7 @@ bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers, ...@@ -270,7 +270,7 @@ bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
return true; return true;
} }
if (headers->response_code() == 200) { if (headers->response_code() != 206) {
DCHECK(byte_range_.IsValid()); DCHECK(byte_range_.IsValid());
sparse_entry_ = false; sparse_entry_ = false;
resource_size_ = entry->GetDataSize(kDataStream); resource_size_ = entry->GetDataSize(kDataStream);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment