[go: nahoru, domu]

Http cache: Extend support for byte range requests.

* Now we handle regular requests (not byte range requests) that
  end up reading a cached entry that stores byte ranges. In this
  case we create a control object (partial_), but it's byte_range_
  member is always invalid because the user is not requesting
  a range.

* Given that we may find stored 206s that are not keeping sparse
  data, we detect that case and handle it.

* Now we may end up reading 206 from disk (and the net) and having
  to change the returned status to be 200 (for regular requests).

* We avoid performing re-validations for each piece of stored data.
  Instead, we consider the whole entry to be revalidated once, and
  read from the cache without asking the server (as far as we can).

* When processing the received headers we now consider receiving
  200 and 416 (instead of 206/304) and we handle inconsistencies
  in the range returned by the server (from what we expect). We
  also handle receiving 206 when we don't expect it.

BUG=12258
TEST=unittests

Review URL: http://codereview.chromium.org/164304

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23123 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index 5035fb9..b7707d1 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -309,6 +309,9 @@
   // copy is valid).  Returns true if able to make the request conditional.
   bool ConditionalizeRequest();
 
+  // Makes sure that a 206 response is expected.  Returns a network error code.
+  bool ValidatePartialResponse(const HttpResponseHeaders* headers);
+
   // Reads data from the network.
   int ReadFromNetwork(IOBuffer* data, int data_len);
 
@@ -335,6 +338,10 @@
   // Called when we are done writing to the cache entry.
   void DoneWritingToEntry(bool success);
 
+  // Deletes the current partial cache entry (sparse), and optionally removes
+  // the control object (partial_).
+  void DoomPartialEntry(bool delete_object);
+
   // Performs the needed work after receiving data from the network.
   int DoNetworkReadCompleted(int result);
 
@@ -798,6 +805,7 @@
     } else {
       // The range is invalid or we cannot handle it properly.
       effective_load_flags_ |= LOAD_DISABLE_CACHE;
+      partial_.reset(NULL);
     }
   }
 
@@ -883,15 +891,23 @@
   return BeginCacheValidation();
 #endif
 
-  if (!partial_.get()) {
+  bool byte_range_requested = partial_.get() != NULL;
+  if (!byte_range_requested) {
     // The request is not for a range, but we have stored just ranges.
-    // TODO(rvargas): Add support for this case.
-    NOTREACHED();
+    partial_.reset(new PartialData());
+    if (!custom_request_.get()) {
+      custom_request_.reset(new HttpRequestInfo(*request_));
+      request_ = custom_request_.get();
+      DCHECK(custom_request_->extra_headers.empty());
+    }
   }
 
-  if (!partial_->UpdateFromStoredHeaders(response_.headers)) {
-    // TODO(rvargas): Handle this error.
-    NOTREACHED();
+  if (!partial_->UpdateFromStoredHeaders(response_.headers,
+                                         entry_->disk_entry)) {
+    // The stored data cannot be used. Get rid of it and restart this request.
+    DoomPartialEntry(!byte_range_requested);
+    mode_ = WRITE;
+    return AddToEntry();
   }
 
   return ContinuePartialCacheValidation();
@@ -899,8 +915,6 @@
 
 int HttpCache::Transaction::ContinuePartialCacheValidation() {
   DCHECK(mode_ == READ_WRITE);
-  // TODO(rvargas): Avoid re-validation of each cached piece.
-
   int rv = partial_->PrepareCacheValidation(entry_->disk_entry,
                                             &custom_request_->extra_headers);
 
@@ -914,6 +928,15 @@
     return HandleResult(rv);
   }
 
+  if (reading_ && partial_->IsCurrentRangeCached()) {
+    rv = ReadFromEntry(read_buf_, read_buf_len_);
+
+    // We are supposed to hanlde errors here.
+    if (rv < 0 && rv != ERR_IO_PENDING)
+      HandleResult(rv);
+    return rv;
+  }
+
   return BeginCacheValidation();
 }
 
@@ -1080,6 +1103,50 @@
   return true;
 }
 
+bool HttpCache::Transaction::ValidatePartialResponse(
+    const HttpResponseHeaders* headers) {
+#ifdef ENABLE_RANGE_SUPPORT
+  bool partial_content = headers->response_code() == 206;
+#else
+  bool partial_content = false;
+#endif
+
+  bool failure = false;
+  if (!partial_content) {
+    if (!partial_.get())
+      return false;
+
+    // TODO(rvargas): Do we need to consider other results here?.
+    if (headers->response_code() == 200 || headers->response_code() == 416)
+      failure = true;
+
+    if (!reading_ && failure) {
+      // We are expecting 206 or 304 because we asked for a range. Given that
+      // the server is refusing the request we'll remove the sparse entry.
+      DoomPartialEntry(true);
+      mode_ = NONE;
+      return false;
+    }
+  }
+
+  if (!failure && partial_.get() && partial_->ResponseHeadersOK(headers))
+    return true;
+
+  // We have a problem. We may or may not be reading already (in which case we
+  // returned the headers), but we'll just pretend that this request is not
+  // using the cache and see what happens. Most likely this is the first
+  // response from the server (it's not changing its mind midway, right?).
+  if (mode_ & WRITE) {
+    DoneWritingToEntry(mode_ != WRITE);
+  } else if (mode_ & READ && entry_) {
+    cache_->DoneReadingFromEntry(entry_, this);
+  }
+
+  entry_ = NULL;
+  mode_ = NONE;
+  return false;
+}
+
 int HttpCache::Transaction::ReadFromNetwork(IOBuffer* data, int data_len) {
   int rv = network_trans_->Read(data, data_len, &network_read_callback_);
   read_buf_ = data;
@@ -1197,6 +1264,14 @@
   mode_ = NONE;  // switch to 'pass through' mode
 }
 
+void HttpCache::Transaction::DoomPartialEntry(bool delete_object) {
+  cache_->DoneWithEntry(entry_, this);
+  cache_->DoomEntry(cache_key_);
+  entry_ = NULL;
+  if (delete_object)
+    partial_.reset(NULL);
+}
+
 void HttpCache::Transaction::OnNetworkInfoAvailable(int result) {
   DCHECK(result != ERR_IO_PENDING);
 
@@ -1211,19 +1286,8 @@
         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());
-        if (!partial_->ResponseHeadersOK(new_response->headers)) {
-          // TODO(rvargas): Handle this error.
-          NOTREACHED();
-        }
-      }
+      bool partial_content = ValidatePartialResponse(new_response->headers);
+
       // Are we expecting a response to a conditional query?
       if (mode_ == READ_WRITE || mode_ == UPDATE) {
         if (new_response->headers->response_code() == 304 || partial_content) {
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 3f9e6675..2efd751 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -485,8 +485,10 @@
                                           std::string* response_status,
                                           std::string* response_headers,
                                           std::string* response_data) {
-  if (request->extra_headers.empty())
+  if (request->extra_headers.empty()) {
+    response_status->assign("HTTP/1.1 416 Requested Range Not Satisfiable");
     return;
+  }
 
   std::vector<net::HttpByteRange> ranges;
   if (!net::HttpUtil::ParseRanges(request->extra_headers, &ranges) ||
@@ -505,9 +507,20 @@
   response_headers->append(content_range);
 
   if (request->extra_headers.find("If-None-Match") == std::string::npos) {
-    EXPECT_EQ(9, end - start);
-    std::string data = StringPrintf("rg: %02d-%02d ", start, end);
+    EXPECT_EQ(9, (end - start) % 10);
+    std::string data;
+    for (int block_start = start; block_start < end; block_start += 10)
+      StringAppendF(&data, "rg: %02d-%02d ", block_start, block_start + 9);
     *response_data = data;
+
+    if (end - start != 9) {
+      // We also have to fix content-length.
+      int len = end - start + 1;
+      EXPECT_EQ(0, len % 10);
+      std::string content_length = StringPrintf("Content-Length: %d\n", len);
+      response_headers->replace(response_headers->find("Content-Length:"),
+                                content_length.size(), content_length);
+    }
   } else {
     response_status->assign("HTTP/1.1 304 Not Modified");
     response_data->clear();
@@ -1634,13 +1647,14 @@
 
 TEST(HttpCache, GET_Crazy206) {
   MockHttpCache cache;
-  AddMockTransaction(&kRangeGET_TransactionOK);
 
   // Test that receiving 206 for a regular request is handled correctly.
 
   // Write to the cache.
   MockTransaction transaction(kRangeGET_TransactionOK);
+  AddMockTransaction(&transaction);
   transaction.request_headers = "";
+  transaction.handler = NULL;
   RunTransactionTest(cache.http_cache(), transaction);
 
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
@@ -1653,7 +1667,7 @@
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
-  RemoveMockTransaction(&kRangeGET_TransactionOK);
+  RemoveMockTransaction(&transaction);
 }
 
 TEST(HttpCache, DISABLED_RangeGET_OK) {
@@ -1706,7 +1720,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
   EXPECT_TRUE(Verify206Response(headers, 20, 59));
-  EXPECT_EQ(6, cache.network_layer()->transaction_count());
+  EXPECT_EQ(5, cache.network_layer()->transaction_count());
   EXPECT_EQ(3, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
@@ -1742,7 +1756,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
   EXPECT_TRUE(Verify206Response(headers, 60, 79));
-  EXPECT_EQ(3, cache.network_layer()->transaction_count());
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
@@ -1751,15 +1765,19 @@
 
 TEST(HttpCache, DISABLED_UnknownRangeGET_2) {
   MockHttpCache cache;
-  AddMockTransaction(&kRangeGET_TransactionOK);
 
   // Test that we can cache range requests when the start or end is unknown.
   // We start with one request from a given point, followed by a suffix request.
+  // We'll also verify that synchronous cache responses work as intended.
 
   std::string headers;
 
-  // Write to the cache (70-79).
   MockTransaction transaction(kRangeGET_TransactionOK);
+  transaction.test_mode = TEST_MODE_SYNC_CACHE_START |
+                          TEST_MODE_SYNC_CACHE_READ;
+  AddMockTransaction(&transaction);
+
+  // Write to the cache (70-79).
   transaction.request_headers = "Range: bytes = 70-\r\n";
   transaction.data = "rg: 70-79 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
@@ -1778,6 +1796,38 @@
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
   EXPECT_TRUE(Verify206Response(headers, 60, 79));
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  RemoveMockTransaction(&transaction);
+}
+
+TEST(HttpCache, DISABLED_GET_Previous206) {
+  MockHttpCache cache;
+  AddMockTransaction(&kRangeGET_TransactionOK);
+
+  // Test that we can handle non-range requests when we have cached a range.
+
+  std::string headers;
+
+  // Write to the cache (40-49).
+  RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
+                                 &headers);
+
+  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Write and read from the cache (0-79), when not asked for a range.
+  MockTransaction transaction(kRangeGET_TransactionOK);
+  transaction.request_headers = "";
+  transaction.data = "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 rg: 40-49 "
+                     "rg: 50-59 rg: 60-69 rg: 70-79 ";
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+
+  EXPECT_EQ(0U, headers.find("HTTP/1.1 200 OK\n"));
   EXPECT_EQ(3, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -1785,6 +1835,130 @@
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
 
+TEST(HttpCache, DISABLED_GET_Previous206_NotSparse) {
+  MockHttpCache cache;
+
+  // Test that we can handle cached 206 responses that are not sparse.
+
+  // Create a disk cache entry that stores 206 headers while not being sparse.
+  disk_cache::Entry* entry;
+  ASSERT_TRUE(cache.disk_cache()->CreateEntry(kSimpleGET_Transaction.url,
+                                              &entry));
+
+  std::string raw_headers(kRangeGET_TransactionOK.status);
+  raw_headers.append("\n");
+  raw_headers.append(kRangeGET_TransactionOK.response_headers);
+  raw_headers = net::HttpUtil::AssembleRawHeaders(raw_headers.data(),
+                                                  raw_headers.size());
+
+  net::HttpResponseInfo response;
+  response.headers = new net::HttpResponseHeaders(raw_headers);
+  net::HttpCache::WriteResponseInfo(entry, &response, true);
+
+  scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(500));
+  int len = static_cast<int>(base::strlcpy(buf->data(),
+                                           kRangeGET_TransactionOK.data, 500));
+  EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+  entry->Close();
+
+  // Now see that we don't use the stored entry.
+  std::string headers;
+  RunTransactionTestWithResponse(cache.http_cache(), kSimpleGET_Transaction,
+                                 &headers);
+
+  // We are expecting a 200.
+  std::string expected_headers(kSimpleGET_Transaction.status);
+  expected_headers.append("\n");
+  expected_headers.append(kSimpleGET_Transaction.response_headers);
+  EXPECT_EQ(expected_headers, headers);
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+TEST(HttpCache, DISABLED_GET_Previous206_NotSparse_2) {
+  MockHttpCache cache;
+  AddMockTransaction(&kRangeGET_TransactionOK);
+
+  // Test that we can handle cached 206 responses that are not sparse. This time
+  // we issue a range request and expect to receive a range.
+
+  // Create a disk cache entry that stores 206 headers while not being sparse.
+  disk_cache::Entry* entry;
+  ASSERT_TRUE(cache.disk_cache()->CreateEntry(kRangeGET_TransactionOK.url,
+                                              &entry));
+
+  std::string raw_headers(kRangeGET_TransactionOK.status);
+  raw_headers.append("\n");
+  raw_headers.append(kRangeGET_TransactionOK.response_headers);
+  raw_headers = net::HttpUtil::AssembleRawHeaders(raw_headers.data(),
+                                                  raw_headers.size());
+
+  net::HttpResponseInfo response;
+  response.headers = new net::HttpResponseHeaders(raw_headers);
+  net::HttpCache::WriteResponseInfo(entry, &response, true);
+
+  scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(500));
+  int len = static_cast<int>(base::strlcpy(buf->data(),
+                                           kRangeGET_TransactionOK.data, 500));
+  EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+  entry->Close();
+
+  // Now see that we don't use the stored entry.
+  std::string headers;
+  RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
+                                 &headers);
+
+  // We are expecting a 206.
+  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(2, cache.disk_cache()->create_count());
+
+  RemoveMockTransaction(&kRangeGET_TransactionOK);
+}
+
+TEST(HttpCache, DISABLED_RangeRequestResultsIn200) {
+  MockHttpCache cache;
+  AddMockTransaction(&kRangeGET_TransactionOK);
+
+  // Test that we can handle a 200 response when dealing with sparse entries.
+
+  std::string headers;
+
+  // Write to the cache (70-79).
+  MockTransaction transaction(kRangeGET_TransactionOK);
+  transaction.request_headers = "Range: bytes = -10\r\n";
+  transaction.data = "rg: 70-79 ";
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+
+  EXPECT_TRUE(Verify206Response(headers, 70, 79));
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Now we'll issue a request that results in a plain 200 response, but to
+  // the to the same URL that we used to store sparse data, and making sure
+  // that we ask for a range.
+  RemoveMockTransaction(&kRangeGET_TransactionOK);
+  MockTransaction transaction2(kSimpleGET_Transaction);
+  transaction2.url = kRangeGET_TransactionOK.url;
+  transaction2.request_headers = kRangeGET_TransactionOK.request_headers;
+  AddMockTransaction(&transaction2);
+
+  RunTransactionTestWithResponse(cache.http_cache(), transaction2, &headers);
+
+  std::string expected_headers(kSimpleGET_Transaction.status);
+  expected_headers.append("\n");
+  expected_headers.append(kSimpleGET_Transaction.response_headers);
+  EXPECT_EQ(expected_headers, headers);
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  RemoveMockTransaction(&transaction2);
+}
+
 TEST(HttpCache, SyncRead) {
   MockHttpCache cache;
 
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc
index f86eff8d..9cbd72f 100644
--- a/net/http/http_response_headers.cc
+++ b/net/http/http_response_headers.cc
@@ -256,6 +256,15 @@
   Parse(new_raw_headers);
 }
 
+void HttpResponseHeaders::ReplaceStatusLine(const std::string& new_status) {
+  // Copy up to the null byte.  This just copies the status line.
+  std::string new_raw_headers(new_status);
+  new_raw_headers.push_back('\0');
+
+  HeaderSet empty_to_remove;
+  MergeWithHeaders(new_raw_headers, empty_to_remove);
+}
+
 void HttpResponseHeaders::Parse(const std::string& raw_input) {
   raw_headers_.reserve(raw_input.size());
 
diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h
index 0552904..547370d9 100644
--- a/net/http/http_response_headers.h
+++ b/net/http/http_response_headers.h
@@ -71,6 +71,10 @@
   // end of the list.
   void AddHeader(const std::string& header);
 
+  // Replaces the current status line with the provided one (|new_status| should
+  // not have any EOL).
+  void ReplaceStatusLine(const std::string& new_status);
+
   // Creates a normalized header string.  The output will be formatted exactly
   // like so:
   //     HTTP/<version> <status_code> <status_text>\n
diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc
index ad03107..9f0cfde 100644
--- a/net/http/http_response_headers_unittest.cc
+++ b/net/http/http_response_headers_unittest.cc
@@ -1504,3 +1504,58 @@
     EXPECT_EQ(string(tests[i].expected_headers), resulting_headers);
   }
 }
+
+TEST(HttpResponseHeadersTest, ReplaceStatus) {
+  const struct {
+    const char* orig_headers;
+    const char* new_status;
+    const char* expected_headers;
+  } tests[] = {
+    { "HTTP/1.1 206 Partial Content\n"
+      "connection: keep-alive\n"
+      "Cache-control: max-age=10000\n"
+      "Content-Length: 450\n",
+
+      "HTTP/1.1 200 OK",
+
+      "HTTP/1.1 200 OK\n"
+      "connection: keep-alive\n"
+      "Cache-control: max-age=10000\n"
+      "Content-Length: 450\n"
+    },
+    { "HTTP/1.1 200 OK\n"
+      "connection: keep-alive\n",
+
+      "HTTP/1.1 304 Not Modified",
+
+      "HTTP/1.1 304 Not Modified\n"
+      "connection: keep-alive\n"
+    },
+    { "HTTP/1.1 200 OK\n"
+      "connection: keep-alive  \n"
+      "Content-Length  : 450   \n"
+      "Cache-control: max-age=10000\n",
+
+      "HTTP/1//1 304 Not Modified",
+
+      "HTTP/1.0 304 Not Modified\n"
+      "connection: keep-alive\n"
+      "Content-Length: 450\n"
+      "Cache-control: max-age=10000\n"
+    },
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+    string orig_headers(tests[i].orig_headers);
+    HeadersToRaw(&orig_headers);
+    scoped_refptr<HttpResponseHeaders> parsed =
+        new HttpResponseHeaders(orig_headers);
+
+    string name(tests[i].new_status);
+    parsed->ReplaceStatusLine(name);
+
+    string resulting_headers;
+    parsed->GetNormalizedHeaders(&resulting_headers);
+    EXPECT_EQ(string(tests[i].expected_headers), resulting_headers);
+  }
+}
diff --git a/net/http/partial_data.cc b/net/http/partial_data.cc
index 8d56966..a4d5976e8 100644
--- a/net/http/partial_data.cc
+++ b/net/http/partial_data.cc
@@ -53,7 +53,7 @@
 
   // Scan the disk cache for the first cached portion within this range.
   int64 range_len = byte_range_.HasLastBytePosition() ?
-      byte_range_.last_byte_position() - current_range_start_ + 1: kint32max;
+      byte_range_.last_byte_position() - current_range_start_ + 1 : kint32max;
   if (range_len > kint32max)
     range_len = kint32max;
   int len = static_cast<int32>(range_len);
@@ -100,20 +100,32 @@
   return final_range_;
 }
 
-bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers) {
+bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
+                                          disk_cache::Entry* entry) {
   std::string length_value;
   resource_size_ = 0;
   if (!headers->GetNormalizedHeader(kLengthHeader, &length_value))
     return false;  // We must have stored the resource length.
 
-  if (!StringToInt64(length_value, &resource_size_))
+  if (!StringToInt64(length_value, &resource_size_) || !resource_size_)
     return false;
 
-  if (resource_size_ && !byte_range_.ComputeBounds(resource_size_))
-    return false;
+  if (byte_range_.IsValid()) {
+    if (!byte_range_.ComputeBounds(resource_size_))
+      return false;
 
-  if (current_range_start_ < 0)
-    current_range_start_ = byte_range_.first_byte_position();
+    if (current_range_start_ < 0)
+      current_range_start_ = byte_range_.first_byte_position();
+  } else {
+    // This is not a range request but we have partial data stored.
+    current_range_start_ = 0;
+    byte_range_.set_last_byte_position(resource_size_ - 1);
+  }
+
+  // Make sure that this is really a sparse entry.
+  int64 n;
+  if (ERR_CACHE_OPERATION_NOT_SUPPORTED == entry->GetAvailableRange(0, 5, &n))
+    return false;
 
   return current_range_start_ >= 0;
 }
@@ -141,7 +153,7 @@
   if (start != current_range_start_)
     return false;
 
-  if (end > byte_range_.last_byte_position())
+  if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
     return false;
 
   return true;
@@ -154,15 +166,23 @@
   headers->RemoveHeader(kLengthHeader);
   headers->RemoveHeader(kRangeHeader);
 
-  DCHECK(byte_range_.HasFirstBytePosition());
-  DCHECK(byte_range_.HasLastBytePosition());
-  headers->AddHeader(StringPrintf("%s: bytes %lld-%lld/%lld", kRangeHeader,
-                                  byte_range_.first_byte_position(),
-                                  byte_range_.last_byte_position(),
-                                  resource_size_));
+  int64 range_len;
+  if (byte_range_.IsValid()) {
+    DCHECK(byte_range_.HasFirstBytePosition());
+    DCHECK(byte_range_.HasLastBytePosition());
+    headers->AddHeader(StringPrintf("%s: bytes %lld-%lld/%lld", kRangeHeader,
+                                    byte_range_.first_byte_position(),
+                                    byte_range_.last_byte_position(),
+                                    resource_size_));
+    range_len = byte_range_.last_byte_position() -
+                byte_range_.first_byte_position() + 1;
+  } else {
+    // TODO(rvargas): Is it safe to change the protocol version?
+    headers->ReplaceStatusLine("HTTP/1.1 200 OK");
+    DCHECK_NE(resource_size_, 0);
+    range_len = resource_size_;
+  }
 
-  int64 range_len = byte_range_.last_byte_position() -
-                    byte_range_.first_byte_position() + 1;
   headers->AddHeader(StringPrintf("%s: %lld", kLengthHeader, range_len));
 }
 
@@ -214,5 +234,4 @@
                                my_end.c_str()));
 }
 
-
 }  // namespace net
diff --git a/net/http/partial_data.h b/net/http/partial_data.h
index 8c3471d..ade720d 100644
--- a/net/http/partial_data.h
+++ b/net/http/partial_data.h
@@ -62,7 +62,8 @@
 
   // Extracts info from headers already stored in the cache. Returns false if
   // there is any problem with the headers or the requested range.
-  bool UpdateFromStoredHeaders(const HttpResponseHeaders* headers);
+  bool UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
+                               disk_cache::Entry* entry);
 
   // Returns true if the response headers match what we expect, false otherwise.
   bool ResponseHeadersOK(const HttpResponseHeaders* headers);