| 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);
|
|
|