diff options
author | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-04 23:36:33 +0000 |
---|---|---|
committer | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-04 23:36:33 +0000 |
commit | 28accfe389231216b005650593fb81857a788b50 (patch) | |
tree | f498a178308fad6b261b9123e3afb758dcb0d63e /net | |
parent | 4ec516851c4f8990c048e6ffa9886dc6074d874a (diff) | |
download | chromium_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')
-rw-r--r-- | net/http/http_cache.cc | 104 | ||||
-rw-r--r-- | net/http/http_cache.h | 17 | ||||
-rw-r--r-- | net/http/http_cache_unittest.cc | 157 | ||||
-rw-r--r-- | net/http/partial_data.cc | 39 | ||||
-rw-r--r-- | net/http/partial_data.h | 9 | ||||
-rw-r--r-- | net/tools/dump_cache/cache_dumper.cc | 7 | ||||
-rw-r--r-- | net/url_request/url_request_view_cache_job.cc | 4 |
7 files changed, 291 insertions, 46 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); }; diff --git a/net/tools/dump_cache/cache_dumper.cc b/net/tools/dump_cache/cache_dumper.cc index 29f7be4..6232d55 100644 --- a/net/tools/dump_cache/cache_dumper.cc +++ b/net/tools/dump_cache/cache_dumper.cc @@ -139,10 +139,15 @@ bool DiskDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset, int len; if (index == 0) { // Stream 0 is the headers. net::HttpResponseInfo response_info; + bool truncated; if (!net::HttpCache::ParseResponseInfo(buf->data(), buf_len, - &response_info)) + &response_info, &truncated)) return false; + // Skip this entry if it was truncated (results in an empty file). + if (truncated) + return true; + // Remove the size headers. response_info.headers->RemoveHeader("transfer-encoding"); response_info.headers->RemoveHeader("content-length"); diff --git a/net/url_request/url_request_view_cache_job.cc b/net/url_request/url_request_view_cache_job.cc index ca82680..112f409 100644 --- a/net/url_request/url_request_view_cache_job.cc +++ b/net/url_request/url_request_view_cache_job.cc @@ -66,7 +66,9 @@ static std::string FormatEntryDetails(disk_cache::Entry* entry) { std::string result = EscapeForHTML(entry->GetKey()); net::HttpResponseInfo response; - if (net::HttpCache::ReadResponseInfo(entry, &response) && response.headers) { + bool truncated; + if (net::HttpCache::ReadResponseInfo(entry, &response, &truncated) && + response.headers) { result.append("<hr><pre>"); result.append(EscapeForHTML(response.headers->GetStatusLine())); result.push_back('\n'); |