summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authorrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-04 23:36:33 +0000
committerrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-04 23:36:33 +0000
commit28accfe389231216b005650593fb81857a788b50 (patch)
treef498a178308fad6b261b9123e3afb758dcb0d63e /net/http
parent4ec516851c4f8990c048e6ffa9886dc6074d874a (diff)
downloadchromium_src-28accfe389231216b005650593fb81857a788b50.zip
chromium_src-28accfe389231216b005650593fb81857a788b50.tar.gz
chromium_src-28accfe389231216b005650593fb81857a788b50.tar.bz2
Http Cache: Add support for resuming downloading a
resource after the original request was interrupted. BUG=8995 TEST=unittests Review URL: http://codereview.chromium.org/197016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25551 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r--net/http/http_cache.cc104
-rw-r--r--net/http/http_cache.h17
-rw-r--r--net/http/http_cache_unittest.cc157
-rw-r--r--net/http/partial_data.cc39
-rw-r--r--net/http/partial_data.h9
5 files changed, 282 insertions, 44 deletions
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index 12d0844..be9dc48 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -64,9 +64,11 @@ enum {
// This bit is set if the response info has vary header data.
RESPONSE_INFO_HAS_VARY_DATA = 1 << 11,
- // TODO(darin): Add other bits to indicate alternate request methods and
- // whether or not we are storing a partial document. For now, we don't
- // support storing those.
+ // This bit is set if the request was cancelled before completion.
+ RESPONSE_INFO_TRUNCATED = 1 << 12,
+
+ // TODO(darin): Add other bits to indicate alternate request methods.
+ // For now, we don't support storing those.
};
//-----------------------------------------------------------------------------
@@ -179,6 +181,7 @@ class HttpCache::Transaction
reading_(false),
invalid_range_(false),
enable_range_support_(enable_range_support),
+ truncated_(false),
read_offset_(0),
effective_load_flags_(0),
final_upload_progress_(0),
@@ -248,6 +251,10 @@ class HttpCache::Transaction
// to the transaction. Returns network error code.
int EntryAvailable(ActiveEntry* entry);
+ // This transaction is being deleted and we are not done writing to the cache.
+ // We need to indicate that the response data was truncated.
+ void AddTruncatedFlag();
+
private:
// This is a helper function used to trigger a completion callback. It may
// only be called if callback_ is non-null.
@@ -327,8 +334,9 @@ class HttpCache::Transaction
// nothing without side-effect.
void WriteToEntry(int index, int offset, IOBuffer* data, int data_len);
- // Called to write response_ to the cache entry.
- void WriteResponseInfoToEntry();
+ // Called to write response_ to the cache entry. |truncated| indicates if the
+ // entry should be marked as incomplete.
+ void WriteResponseInfoToEntry(bool truncated);
// Called to append response data to the cache entry.
void AppendResponseDataToEntry(IOBuffer* data, int data_len);
@@ -383,6 +391,7 @@ class HttpCache::Transaction
bool reading_; // We are already reading.
bool invalid_range_; // We may bypass the cache for this request.
bool enable_range_support_;
+ bool truncated_; // We don't have all the response data.
scoped_refptr<IOBuffer> read_buf_;
int read_buf_len_;
int read_offset_;
@@ -398,7 +407,11 @@ class HttpCache::Transaction
HttpCache::Transaction::~Transaction() {
if (!revoked()) {
if (entry_) {
- cache_->DoneWithEntry(entry_, this);
+ bool cancel_request = reading_ && !partial_.get() &&
+ enable_range_support_ &&
+ response_.headers->response_code() == 200;
+
+ cache_->DoneWithEntry(entry_, this, cancel_request);
} else {
cache_->RemovePendingTransaction(this);
}
@@ -558,12 +571,12 @@ int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len,
mode_ = NONE;
}
+ reading_ = true;
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);
@@ -697,6 +710,12 @@ int HttpCache::Transaction::EntryAvailable(ActiveEntry* entry) {
return rv;
}
+void HttpCache::Transaction::AddTruncatedFlag() {
+ DCHECK(mode_ & WRITE);
+ truncated_ = true;
+ WriteResponseInfoToEntry(true);
+}
+
void HttpCache::Transaction::DoCallback(int rv) {
DCHECK(rv != ERR_IO_PENDING);
DCHECK(callback_);
@@ -877,6 +896,10 @@ int HttpCache::Transaction::BeginCacheRead() {
return HandleResult(ERR_CACHE_MISS);
}
+ // We don't have the whole resource.
+ if (truncated_)
+ return HandleResult(ERR_CACHE_MISS);
+
return HandleResult(rv);
}
@@ -908,7 +931,8 @@ int HttpCache::Transaction::BeginPartialCacheValidation() {
return HandleResult(rv);
}
- if (response_.headers->response_code() != 206 && !partial_.get())
+ if (response_.headers->response_code() != 206 && !partial_.get() &&
+ !truncated_)
return BeginCacheValidation();
if (!enable_range_support_)
@@ -925,8 +949,8 @@ int HttpCache::Transaction::BeginPartialCacheValidation() {
}
}
- if (!partial_->UpdateFromStoredHeaders(response_.headers,
- entry_->disk_entry)) {
+ if (!partial_->UpdateFromStoredHeaders(response_.headers, entry_->disk_entry,
+ truncated_)) {
// The stored data cannot be used. Get rid of it and restart this request.
DoomPartialEntry(!byte_range_requested);
mode_ = WRITE;
@@ -987,9 +1011,8 @@ int HttpCache::Transaction::BeginExternallyConditionalizedRequest() {
external_validation_.type_info().related_response_header_name,
&validator);
- if (response_.headers->response_code() != 200 ||
- validator.empty() ||
- validator != external_validation_.value) {
+ if (response_.headers->response_code() != 200 || truncated_ ||
+ validator.empty() || validator != external_validation_.value) {
// The externally conditionalized request is not a validation request
// for our existing cache entry. Proceed with caching disabled.
DoneWritingToEntry(true);
@@ -1251,7 +1274,7 @@ int HttpCache::Transaction::ReadFromEntry(IOBuffer* data, int data_len) {
int HttpCache::Transaction::ReadResponseInfoFromEntry() {
DCHECK(entry_);
- if (!HttpCache::ReadResponseInfo(entry_->disk_entry, &response_))
+ if (!HttpCache::ReadResponseInfo(entry_->disk_entry, &response_, &truncated_))
return ERR_CACHE_READ_FAILURE;
return OK;
}
@@ -1274,7 +1297,7 @@ void HttpCache::Transaction::WriteToEntry(int index, int offset,
}
}
-void HttpCache::Transaction::WriteResponseInfoToEntry() {
+void HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) {
if (!entry_)
return;
@@ -1298,8 +1321,12 @@ void HttpCache::Transaction::WriteResponseInfoToEntry() {
// headers; when in record mode, record everything.
bool skip_transient_headers = (cache_->mode() != RECORD);
+ if (truncated) {
+ DCHECK_EQ(200, response_.headers->response_code());
+ }
+
if (!HttpCache::WriteResponseInfo(entry_->disk_entry, &response_,
- skip_transient_headers)) {
+ skip_transient_headers, truncated)) {
DLOG(ERROR) << "failed to write response info to cache";
DoneWritingToEntry(false);
}
@@ -1336,7 +1363,7 @@ void HttpCache::Transaction::DoneWritingToEntry(bool success) {
}
void HttpCache::Transaction::DoomPartialEntry(bool delete_object) {
- cache_->DoneWithEntry(entry_, this);
+ cache_->DoneWithEntry(entry_, this, false);
cache_->DoomEntry(cache_key_);
entry_ = NULL;
if (delete_object)
@@ -1429,7 +1456,7 @@ void HttpCache::Transaction::OnNetworkInfoAvailable(int result) {
auth_response_ = *new_response;
} else {
bool partial_content = ValidatePartialResponse(new_response->headers);
- if (partial_content && mode_ == READ_WRITE &&
+ if (partial_content && mode_ == READ_WRITE && !truncated_ &&
response_.headers->response_code() == 200) {
// We have stored the full entry, but it changed and the server is
// sending a range. We have to delete the old entry.
@@ -1452,7 +1479,7 @@ void HttpCache::Transaction::OnNetworkInfoAvailable(int result) {
// If we are already reading, we already updated the headers for
// this request; doing it again will change Content-Length.
if (!reading_)
- WriteResponseInfoToEntry();
+ WriteResponseInfoToEntry(false);
}
if (mode_ == UPDATE) {
@@ -1485,7 +1512,7 @@ void HttpCache::Transaction::OnNetworkInfoAvailable(int result) {
partial_->FixContentLength(new_response->headers);
response_ = *new_response;
- WriteResponseInfoToEntry();
+ WriteResponseInfoToEntry(truncated_);
// Truncate response data.
TruncateResponseData();
@@ -1495,8 +1522,7 @@ void HttpCache::Transaction::OnNetworkInfoAvailable(int result) {
if (response_.headers->IsRedirect(NULL))
DoneWritingToEntry(true);
}
- if (reading_) {
- DCHECK(partial_.get());
+ if (reading_ && partial_.get()) {
if (network_trans_.get()) {
result = ReadFromNetwork(read_buf_, read_buf_len_);
} else {
@@ -1636,7 +1662,8 @@ void HttpCache::Suspend(bool suspend) {
// static
bool HttpCache::ParseResponseInfo(const char* data, int len,
- HttpResponseInfo* response_info) {
+ HttpResponseInfo* response_info,
+ bool* response_truncated) {
Pickle pickle(data, len);
void* iter = NULL;
@@ -1690,12 +1717,15 @@ bool HttpCache::ParseResponseInfo(const char* data, int len,
return false;
}
+ *response_truncated = (flags & RESPONSE_INFO_TRUNCATED) ? true : false;
+
return true;
}
// static
bool HttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry,
- HttpResponseInfo* response_info) {
+ HttpResponseInfo* response_info,
+ bool* response_truncated) {
int size = disk_entry->GetDataSize(kResponseInfoIndex);
scoped_refptr<IOBuffer> buffer = new IOBuffer(size);
@@ -1705,13 +1735,15 @@ bool HttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry,
return false;
}
- return ParseResponseInfo(buffer->data(), size, response_info);
+ return ParseResponseInfo(buffer->data(), size, response_info,
+ response_truncated);
}
// static
bool HttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry,
const HttpResponseInfo* response_info,
- bool skip_transient_headers) {
+ bool skip_transient_headers,
+ bool response_truncated) {
int flags = RESPONSE_INFO_VERSION;
if (response_info->ssl_info.cert) {
flags |= RESPONSE_INFO_HAS_CERT;
@@ -1721,6 +1753,8 @@ bool HttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry,
flags |= RESPONSE_INFO_HAS_SECURITY_BITS;
if (response_info->vary_data.is_valid())
flags |= RESPONSE_INFO_HAS_VARY_DATA;
+ if (response_truncated)
+ flags |= RESPONSE_INFO_TRUNCATED;
Pickle pickle;
pickle.WriteInt(flags);
@@ -1942,7 +1976,8 @@ int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
return trans->EntryAvailable(entry);
}
-void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans) {
+void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
+ bool cancel) {
// If we already posted a task to move on to the next transaction and this was
// the writer, there is nothing to cancel.
if (entry->will_process_pending_queue && entry->readers.empty())
@@ -1951,8 +1986,19 @@ void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans) {
if (entry->writer) {
DCHECK(trans == entry->writer);
- // Assume that this is not a successful write.
- DoneWritingToEntry(entry, false);
+ // Assume there was a failure.
+ bool success = false;
+ if (cancel) {
+ DCHECK(entry->disk_entry);
+ // This is a successful operation in the sense that we want to keep the
+ // entry.
+ success = true;
+ // Double check that there is something worth keeping.
+ if (!entry->disk_entry->GetDataSize(kResponseContentIndex))
+ success = false;
+ trans->AddTruncatedFlag();
+ }
+ DoneWritingToEntry(entry, success);
} else {
DoneReadingFromEntry(entry, trans);
}
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index f1807b7..b6599b2 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -97,18 +97,23 @@ class HttpCache : public HttpTransactionFactory {
virtual HttpCache* GetCache();
virtual void Suspend(bool suspend);
- // Helper function for reading response info from the disk cache.
+ // Helper function for reading response info from the disk cache. If the
+ // cache doesn't have the whole resource *|request_truncated| is set to true.
static bool ReadResponseInfo(disk_cache::Entry* disk_entry,
- HttpResponseInfo* response_info);
+ HttpResponseInfo* response_info,
+ bool* response_truncated);
- // Helper function for writing response info into the disk cache.
+ // Helper function for writing response info into the disk cache. If the
+ // cache doesn't have the whole resource |request_truncated| should be true.
static bool WriteResponseInfo(disk_cache::Entry* disk_entry,
const HttpResponseInfo* response_info,
- bool skip_transient_headers);
+ bool skip_transient_headers,
+ bool response_truncated);
// Given a header data blob, convert it to a response info object.
static bool ParseResponseInfo(const char* data, int len,
- HttpResponseInfo* response_info);
+ HttpResponseInfo* response_info,
+ bool* response_truncated);
// Get/Set the cache's mode.
void set_mode(Mode value) { mode_ = value; }
@@ -162,7 +167,7 @@ class HttpCache : public HttpTransactionFactory {
ActiveEntry* CreateEntry(const std::string& cache_key);
void DestroyEntry(ActiveEntry* entry);
int AddTransactionToEntry(ActiveEntry* entry, Transaction* trans);
- void DoneWithEntry(ActiveEntry* entry, Transaction* trans);
+ void DoneWithEntry(ActiveEntry* entry, Transaction* trans, bool cancel);
void DoneWritingToEntry(ActiveEntry* entry, bool success);
void DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans);
void ConvertWriterToReader(ActiveEntry* entry);
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 8d01b6a..a72c36d 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -2013,7 +2013,7 @@ TEST(HttpCache, GET_Previous206_NotSparse) {
net::HttpResponseInfo response;
response.headers = new net::HttpResponseHeaders(raw_headers);
- net::HttpCache::WriteResponseInfo(entry, &response, true);
+ EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, false));
scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(500));
int len = static_cast<int>(base::strlcpy(buf->data(),
@@ -2056,7 +2056,7 @@ TEST(HttpCache, RangeGET_Previous206_NotSparse_2) {
net::HttpResponseInfo response;
response.headers = new net::HttpResponseHeaders(raw_headers);
- net::HttpCache::WriteResponseInfo(entry, &response, true);
+ EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, false));
scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(500));
int len = static_cast<int>(base::strlcpy(buf->data(),
@@ -2238,6 +2238,159 @@ TEST(HttpCache, RangeGET_OK_LoadOnlyFromCache) {
}
#endif
+// Tests the handling of the "truncation" flag.
+TEST(HttpCache, WriteResponseInfo_Truncated) {
+ MockHttpCache cache;
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache.disk_cache()->CreateEntry("http://www.google.com", &entry));
+
+ std::string headers("HTTP/1.1 200 OK");
+ headers = net::HttpUtil::AssembleRawHeaders(headers.data(), headers.size());
+ net::HttpResponseInfo response;
+ response.headers = new net::HttpResponseHeaders(headers);
+
+ // Set the last argument for this to be an incomplete request.
+ EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, true));
+ bool truncated = false;
+ EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+ EXPECT_TRUE(truncated);
+
+ // And now test the opposite case.
+ EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, false));
+ truncated = true;
+ EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+ EXPECT_FALSE(truncated);
+ entry->Close();
+}
+
+// Tests that we delete an entry when the request is cancelled before starting
+// to read from the network.
+TEST(HttpCache, DoomOnDestruction) {
+ MockHttpCache cache;
+ cache.http_cache()->set_enable_range_support(true);
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ Context* c = new Context(cache.http_cache()->CreateTransaction());
+
+ int rv = c->trans->Start(&request, &c->callback, NULL);
+ if (rv == net::ERR_IO_PENDING)
+ c->result = c->callback.WaitForResult();
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // Destroy the transaction. We only have the headers so we should delete this
+ // entry.
+ delete c;
+
+ RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+// Tests that we mark an entry as incomplete when the request is cancelled.
+TEST(HttpCache, Set_Truncated_Flag) {
+ MockHttpCache cache;
+ cache.http_cache()->set_enable_range_support(true);
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ Context* c = new Context(cache.http_cache()->CreateTransaction());
+
+ int rv = c->trans->Start(&request, &c->callback, NULL);
+ if (rv == net::ERR_IO_PENDING)
+ rv = c->callback.WaitForResult();
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // Make sure that the entry has some data stored.
+ scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(10);
+ rv = c->trans->Read(buf, buf->size(), &c->callback);
+ if (rv == net::ERR_IO_PENDING)
+ rv = c->callback.WaitForResult();
+ EXPECT_EQ(buf->size(), rv);
+
+ // Destroy the transaction.
+ delete c;
+
+ // Verify that the entry is marked as incomplete.
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache.disk_cache()->OpenEntry(kSimpleGET_Transaction.url,
+ &entry));
+ net::HttpResponseInfo response;
+ bool truncated = false;
+ EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+ EXPECT_TRUE(truncated);
+ entry->Close();
+}
+
+// Tests that we can continue with a request that was interrupted.
+TEST(HttpCache, GET_IncompleteResource) {
+ MockHttpCache cache;
+ cache.http_cache()->set_enable_range_support(true);
+ AddMockTransaction(&kRangeGET_TransactionOK);
+
+ // Create a disk cache entry that stores an incomplete resource.
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache.disk_cache()->CreateEntry(kRangeGET_TransactionOK.url,
+ &entry));
+
+ // Content-length will be intentionally bogus.
+ std::string raw_headers("HTTP/1.1 200 OK\n"
+ "Last-Modified: something\n"
+ "ETag: \"foo\"\n"
+ "Accept-Ranges: bytes\n"
+ "Content-Length: 10\n");
+ raw_headers = net::HttpUtil::AssembleRawHeaders(raw_headers.data(),
+ raw_headers.size());
+
+ net::HttpResponseInfo response;
+ response.headers = new net::HttpResponseHeaders(raw_headers);
+ // Set the last argument for this to be an incomplete request.
+ EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, true));
+
+ scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(100));
+ int len = static_cast<int>(base::strlcpy(buf->data(),
+ "rg: 00-09 rg: 10-19 ", 100));
+ EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+
+ // Now make a regular request.
+ std::string headers;
+ 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);
+
+ // We update the headers with the ones received while revalidating.
+ std::string expected_headers(
+ "HTTP/1.1 200 OK\n"
+ "Last-Modified: Sat, 18 Apr 2009 01:10:43 GMT\n"
+ "Accept-Ranges: bytes\n"
+ "ETag: \"foo\"\n"
+ "Content-Length: 10\n");
+
+ 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(&kRangeGET_TransactionOK);
+
+ // Verify that the disk entry was updated.
+ EXPECT_EQ(80, entry->GetDataSize(1));
+ bool truncated = true;
+ EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+ EXPECT_FALSE(truncated);
+ entry->Close();
+}
+
TEST(HttpCache, SyncRead) {
MockHttpCache cache;
diff --git a/net/http/partial_data.cc b/net/http/partial_data.cc
index 70d7d18..fcd7808 100644
--- a/net/http/partial_data.cc
+++ b/net/http/partial_data.cc
@@ -65,6 +65,12 @@ int PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
if (sparse_entry_) {
cached_min_len_ = entry->GetAvailableRange(current_range_start_, len,
&cached_start_);
+ } else if (truncated_) {
+ if (!current_range_start_) {
+ // Update the cached range only the first time.
+ cached_min_len_ = static_cast<int32>(byte_range_.first_byte_position());
+ cached_start_ = 0;
+ }
} else {
cached_min_len_ = len;
cached_start_ = current_range_start_;
@@ -80,7 +86,8 @@ int PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
if (!cached_min_len_) {
// We don't have anything else stored.
final_range_ = true;
- cached_start_ = current_range_start_ + len;
+ cached_start_ =
+ byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
}
if (current_range_start_ == cached_start_) {
@@ -108,9 +115,24 @@ bool PartialData::IsLastRange() const {
}
bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
- disk_cache::Entry* entry) {
- std::string length_value;
+ disk_cache::Entry* entry,
+ bool truncated) {
resource_size_ = 0;
+ if (truncated) {
+ DCHECK_EQ(headers->response_code(), 200);
+ truncated_ = true;
+ sparse_entry_ = false;
+
+ // We don't have the real length and the user may be trying to create a
+ // sparse entry so let's not write to this entry.
+ if (byte_range_.IsValid())
+ return false;
+
+ byte_range_.set_first_byte_position(entry->GetDataSize(kDataStream));
+ current_range_start_ = 0;
+ return true;
+ }
+
if (headers->response_code() == 200) {
DCHECK(byte_range_.IsValid());
sparse_entry_ = false;
@@ -118,6 +140,7 @@ bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
return true;
}
+ std::string length_value;
if (!headers->GetNormalizedHeader(kLengthHeader, &length_value))
return false; // We must have stored the resource length.
@@ -134,6 +157,8 @@ bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
bool PartialData::IsRequestedRangeOK() {
if (byte_range_.IsValid()) {
+ if (truncated_)
+ return true;
if (!byte_range_.ComputeBounds(resource_size_))
return false;
@@ -154,6 +179,9 @@ bool PartialData::IsRequestedRangeOK() {
bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
if (headers->response_code() == 304) {
+ if (truncated_)
+ return true;
+
// We must have a complete range here.
return byte_range_.HasFirstBytePosition() &&
byte_range_.HasLastBytePosition();
@@ -191,6 +219,9 @@ bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
// Just assume that everything is fine and say that we are returning what was
// requested.
void PartialData::FixResponseHeaders(HttpResponseHeaders* headers) {
+ if (truncated_)
+ return;
+
headers->RemoveHeader(kLengthHeader);
headers->RemoveHeader(kRangeHeader);
@@ -249,7 +280,7 @@ int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
return ERR_INVALID_ARGUMENT;
return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
- data, data_len, callback, false);
+ data, data_len, callback, true);
}
}
diff --git a/net/http/partial_data.h b/net/http/partial_data.h
index 460ff21..fcbec57 100644
--- a/net/http/partial_data.h
+++ b/net/http/partial_data.h
@@ -32,7 +32,8 @@ class IOBuffer;
class PartialData {
public:
PartialData()
- : range_present_(false), final_range_(false), sparse_entry_(true) {}
+ : range_present_(false), final_range_(false), sparse_entry_(true),
+ truncated_(false) {}
~PartialData() {}
// Performs initialization of the object by parsing the request |headers|
@@ -62,9 +63,10 @@ class PartialData {
bool IsLastRange() const;
// Extracts info from headers already stored in the cache. Returns false if
- // there is any problem with the headers.
+ // there is any problem with the headers. |truncated| should be true if we
+ // have an incomplete 200 entry.
bool UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
- disk_cache::Entry* entry);
+ disk_cache::Entry* entry, bool truncated);
// Returns true if the requested range is valid given the stored data.
bool IsRequestedRangeOK();
@@ -111,6 +113,7 @@ class PartialData {
bool range_present_; // True if next range entry is already stored.
bool final_range_;
bool sparse_entry_;
+ bool truncated_; // We have an incomplete 200 stored.
DISALLOW_COPY_AND_ASSIGN(PartialData);
};