Index: webkit/appcache/appcache_update_job.cc |
=================================================================== |
--- webkit/appcache/appcache_update_job.cc (revision 52941) |
+++ webkit/appcache/appcache_update_job.cc (working copy) |
@@ -13,7 +13,6 @@ |
#include "net/http/http_request_headers.h" |
#include "webkit/appcache/appcache_group.h" |
#include "webkit/appcache/appcache_policy.h" |
-#include "webkit/appcache/appcache_response.h" |
namespace appcache { |
@@ -43,8 +42,8 @@ |
} |
void SetUpResponseWriter(AppCacheResponseWriter* writer, |
- AppCacheUpdateJob* update, |
- URLRequest* request) { |
+ AppCacheUpdateJob* update, |
+ URLRequest* request) { |
DCHECK(!response_writer_.get()); |
response_writer_.reset(writer); |
update_job_ = update; |
@@ -60,6 +59,9 @@ |
scoped_refptr<net::IOBuffer> buffer_; |
int retry_503_attempts_; |
+ // The entry from the newest cache for this url, used for 304 responses. |
+ AppCacheEntry existing_entry_; |
+ |
// Info needed to write responses to storage and process callbacks. |
scoped_ptr<AppCacheResponseWriter> response_writer_; |
AppCacheUpdateJob* update_job_; |
@@ -159,6 +161,10 @@ |
policy_callback_->Cancel(); |
} |
+UpdateJobInfo* AppCacheUpdateJob::GetUpdateJobInfo(URLRequest* request) { |
+ return static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
+} |
+ |
void AppCacheUpdateJob::StartUpdate(AppCacheHost* host, |
const GURL& new_master_resource) { |
DCHECK(group_->update_job() == this); |
@@ -285,44 +291,41 @@ |
service_->storage()->LoadResponseInfo(manifest_url_, |
entry->response_id(), this); |
} else { |
- AddHttpHeadersAndFetch(manifest_url_request_, NULL); |
+ manifest_url_request_->Start(); |
} |
} else { |
DCHECK(internal_state_ == REFETCH_MANIFEST); |
DCHECK(manifest_response_info_.get()); |
- AddHttpHeadersAndFetch(manifest_url_request_, |
- manifest_response_info_.get()); |
+ AddConditionalHeaders(manifest_url_request_, |
+ manifest_response_info_.get()); |
+ manifest_url_request_->Start(); |
} |
} |
-void AppCacheUpdateJob::AddHttpHeadersAndFetch( |
+void AppCacheUpdateJob::AddConditionalHeaders( |
URLRequest* request, const net::HttpResponseInfo* info) { |
- DCHECK(request); |
- if (info) { |
- net::HttpRequestHeaders extra_headers; |
+ DCHECK(request && info); |
+ net::HttpRequestHeaders extra_headers; |
- // Add If-Modified-Since header if response info has Last-Modified header. |
- const std::string last_modified = "Last-Modified"; |
- std::string last_modified_value; |
- info->headers->EnumerateHeader(NULL, last_modified, &last_modified_value); |
- if (!last_modified_value.empty()) { |
- extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince, |
- last_modified_value); |
- } |
+ // Add If-Modified-Since header if response info has Last-Modified header. |
+ const std::string last_modified = "Last-Modified"; |
+ std::string last_modified_value; |
+ info->headers->EnumerateHeader(NULL, last_modified, &last_modified_value); |
+ if (!last_modified_value.empty()) { |
+ extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince, |
+ last_modified_value); |
+ } |
- // Add If-None-Match header if resposne info has ETag header. |
- const std::string etag = "ETag"; |
- std::string etag_value; |
- info->headers->EnumerateHeader(NULL, etag, &etag_value); |
- if (!etag_value.empty()) { |
- extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch, |
- etag_value); |
- } |
- |
- if (!extra_headers.IsEmpty()) |
- request->SetExtraRequestHeaders(extra_headers); |
+ // Add If-None-Match header if resposne info has ETag header. |
+ const std::string etag = "ETag"; |
+ std::string etag_value; |
+ info->headers->EnumerateHeader(NULL, etag, &etag_value); |
+ if (!etag_value.empty()) { |
+ extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch, |
+ etag_value); |
} |
- request->Start(); |
+ if (!extra_headers.IsEmpty()) |
+ request->SetExtraRequestHeaders(extra_headers); |
} |
void AppCacheUpdateJob::OnResponseStarted(URLRequest *request) { |
@@ -330,8 +333,7 @@ |
(request->GetResponseCode() / 100) == 2) { |
// Write response info to storage for URL fetches. Wait for async write |
// completion before reading any response data. |
- UpdateJobInfo* info = |
- static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
+ UpdateJobInfo* info = GetUpdateJobInfo(request); |
if (info->type_ == UpdateJobInfo::URL_FETCH || |
info->type_ == UpdateJobInfo::MASTER_ENTRY_FETCH) { |
info->SetUpResponseWriter( |
@@ -357,8 +359,7 @@ |
} |
int bytes_read = 0; |
- UpdateJobInfo* info = |
- static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
+ UpdateJobInfo* info = GetUpdateJobInfo(request); |
request->Read(info->buffer_, kBufferSize, &bytes_read); |
OnReadCompleted(request, bytes_read); |
} |
@@ -366,9 +367,7 @@ |
void AppCacheUpdateJob::OnReadCompleted(URLRequest* request, int bytes_read) { |
bool data_consumed = true; |
if (request->status().is_success() && bytes_read > 0) { |
- UpdateJobInfo* info = |
- static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
- |
+ UpdateJobInfo* info = GetUpdateJobInfo(request); |
data_consumed = ConsumeResponseData(request, info, bytes_read); |
if (data_consumed) { |
bytes_read = 0; |
@@ -439,8 +438,7 @@ |
return; |
} |
- UpdateJobInfo* info = |
- static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
+ UpdateJobInfo* info = GetUpdateJobInfo(request); |
switch (info->type_) { |
case UpdateJobInfo::MANIFEST_FETCH: |
HandleManifestFetchCompleted(request); |
@@ -462,8 +460,7 @@ |
} |
bool AppCacheUpdateJob::RetryRequest(URLRequest* request) { |
- UpdateJobInfo* info = |
- static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
+ UpdateJobInfo* info = GetUpdateJobInfo(request); |
if (info->retry_503_attempts_ >= kMax503Retries) { |
return false; |
} |
@@ -475,6 +472,7 @@ |
URLRequest* retry = new URLRequest(url, this); |
UpdateJobInfo* retry_info = new UpdateJobInfo(info->type_); |
retry_info->retry_503_attempts_ = info->retry_503_attempts_ + 1; |
+ retry_info->existing_entry_ = info->existing_entry_; |
retry->SetUserData(this, retry_info); |
retry->set_context(request->context()); |
retry->set_load_flags(request->load_flags()); |
@@ -598,6 +596,7 @@ |
void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) { |
DCHECK(internal_state_ == DOWNLOADING); |
+ UpdateJobInfo* info = GetUpdateJobInfo(request); |
const GURL& url = request->original_url(); |
pending_url_fetches_.erase(url); |
@@ -608,14 +607,11 @@ |
? request->GetResponseCode() : -1; |
AppCacheEntry& entry = url_file_list_.find(url)->second; |
- if (request->status().is_success() && (response_code / 100 == 2)) { |
+ if (response_code / 100 == 2) { |
// Associate storage with the new entry. |
- UpdateJobInfo* info = |
- static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
DCHECK(info->response_writer_.get()); |
entry.set_response_id(info->response_writer_->response_id()); |
entry.set_response_size(info->response_writer_->amount_written()); |
- |
if (!inprogress_cache_->AddOrModifyEntry(url, entry)) |
duplicate_response_ids_.push_back(entry.response_id()); |
@@ -629,22 +625,29 @@ |
<< " os_error: " << request->status().os_error() |
<< " response code: " << response_code; |
if (entry.IsExplicit() || entry.IsFallback()) { |
- const char* kFormatString = "Resource fetch failed (%d) %s"; |
- const std::string message = StringPrintf(kFormatString, response_code, |
- request->url().spec().c_str()); |
- HandleCacheFailure(message); |
- return; |
+ if (response_code == 304 && info->existing_entry_.has_response_id()) { |
+ // Copy the response from the newest complete cache. |
rvargas (doing something else)
2010/07/20 19:17:18
maybe change from the newest complete cache -> fro
|
+ entry.set_response_id(info->existing_entry_.response_id()); |
+ entry.set_response_size(info->existing_entry_.response_size()); |
+ inprogress_cache_->AddOrModifyEntry(url, entry); |
+ } else { |
+ const char* kFormatString = "Resource fetch failed (%d) %s"; |
+ const std::string message = StringPrintf(kFormatString, response_code, |
+ request->url().spec().c_str()); |
+ HandleCacheFailure(message); |
+ return; |
+ } |
} else if (response_code == 404 || response_code == 410) { |
// Entry is skipped. They are dropped from the cache. |
- } else if (update_type_ == UPGRADE_ATTEMPT) { |
+ } else if (update_type_ == UPGRADE_ATTEMPT && |
+ info->existing_entry_.has_response_id()) { |
// Copy the response from the newest complete cache. |
- AppCache* cache = group_->newest_complete_cache(); |
- AppCacheEntry* copy = cache->GetEntry(url); |
- if (copy) { |
- entry.set_response_id(copy->response_id()); |
- entry.set_response_size(copy->response_size()); |
- inprogress_cache_->AddOrModifyEntry(url, entry); |
- } |
+ // TODO(michaeln): Not sure this is a good idea. This is spec compliant |
+ // but the old resource may or may not be compatible with the new contents |
+ // of the cache. Impossible to know one way or the other. |
+ entry.set_response_id(info->existing_entry_.response_id()); |
+ entry.set_response_size(info->existing_entry_.response_size()); |
+ inprogress_cache_->AddOrModifyEntry(url, entry); |
} |
} |
@@ -676,8 +679,7 @@ |
// Section 6.9.4. No update case: step 7.3, else step 22. |
if (response_code / 100 == 2) { |
// Add fetched master entry to the appropriate cache. |
- UpdateJobInfo* info = |
- static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
+ UpdateJobInfo* info = GetUpdateJobInfo(request); |
AppCache* cache = inprogress_cache_ ? inprogress_cache_.get() : |
group_->newest_complete_cache(); |
DCHECK(info->response_writer_.get()); |
@@ -943,7 +945,7 @@ |
AppCache::EntryMap::value_type(url, AppCacheEntry(type))); |
if (ret.second) |
- urls_to_fetch_.push_back(UrlsToFetch(url, false)); |
+ urls_to_fetch_.push_back(UrlToFetch(url, false, NULL)); |
else |
ret.first->second.add_types(type); // URL already exists. Merge types. |
} |
@@ -956,30 +958,46 @@ |
// each fetch completes. |
while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches && |
!urls_to_fetch_.empty()) { |
- const GURL url = urls_to_fetch_.front().first; |
- bool storage_checked = urls_to_fetch_.front().second; |
+ UrlToFetch url_to_fetch = urls_to_fetch_.front(); |
urls_to_fetch_.pop_front(); |
- AppCache::EntryMap::iterator it = url_file_list_.find(url); |
+ AppCache::EntryMap::iterator it = url_file_list_.find(url_to_fetch.url); |
DCHECK(it != url_file_list_.end()); |
AppCacheEntry& entry = it->second; |
if (ShouldSkipUrlFetch(entry)) { |
- NotifyAllProgress(url); |
+ NotifyAllProgress(url_to_fetch.url); |
++url_fetches_completed_; |
- } else if (AlreadyFetchedEntry(url, entry.types())) { |
- NotifyAllProgress(url); |
+ } else if (AlreadyFetchedEntry(url_to_fetch.url, entry.types())) { |
+ NotifyAllProgress(url_to_fetch.url); |
++url_fetches_completed_; // saved a URL request |
- } else if (!storage_checked && MaybeLoadFromNewestCache(url, entry)) { |
+ } else if (!url_to_fetch.storage_checked && |
+ MaybeLoadFromNewestCache(url_to_fetch.url, entry)) { |
// Continues asynchronously after data is loaded from newest cache. |
} else { |
+ UpdateJobInfo* info = new UpdateJobInfo(UpdateJobInfo::URL_FETCH); |
+ const net::HttpResponseInfo* http_info = NULL; |
+ if (url_to_fetch.existing_response_info.get()) { |
+ DCHECK(group_->newest_complete_cache()); |
+ AppCacheEntry* existing_entry = |
+ group_->newest_complete_cache()->GetEntry(url_to_fetch.url); |
+ DCHECK(existing_entry); |
+ DCHECK(existing_entry->response_id() == |
+ url_to_fetch.existing_response_info->response_id()); |
+ info->existing_entry_ = *existing_entry; |
+ http_info = url_to_fetch.existing_response_info->http_response_info(); |
+ } |
+ |
// Send URL request for the resource. |
- URLRequest* request = new URLRequest(url, this); |
- request->SetUserData(this, new UpdateJobInfo(UpdateJobInfo::URL_FETCH)); |
+ URLRequest* request = new URLRequest(url_to_fetch.url, this); |
+ request->SetUserData(this, info); |
request->set_context(service_->request_context()); |
request->set_load_flags( |
request->load_flags() | net::LOAD_DISABLE_INTERCEPT); |
+ if (http_info) |
+ AddConditionalHeaders(request, http_info); |
request->Start(); |
- pending_url_fetches_.insert(PendingUrlFetches::value_type(url, request)); |
+ pending_url_fetches_.insert( |
+ PendingUrlFetches::value_type(url_to_fetch.url, request)); |
} |
} |
} |
@@ -1161,7 +1179,9 @@ |
// Needed response info for a manifest fetch request. |
if (internal_state_ == FETCH_MANIFEST) { |
- AddHttpHeadersAndFetch(manifest_url_request_, http_info); |
+ if (http_info) |
+ AddConditionalHeaders(manifest_url_request_, http_info); |
+ manifest_url_request_->Start(); |
return; |
} |
@@ -1170,7 +1190,7 @@ |
const GURL& url = found->second; |
if (!http_info) { |
- LoadFromNewestCacheFailed(url); // no response found |
+ LoadFromNewestCacheFailed(url, NULL); // no response found |
} else { |
// Check if response can be re-used according to HTTP caching semantics. |
// Responses with a "vary" header get treated as expired. |
@@ -1182,7 +1202,7 @@ |
base::Time::Now()) || |
http_info->headers->EnumerateHeader(&iter, name, &value)) { |
// TODO(michaeln): Make a conditional request when we can in this case. |
- LoadFromNewestCacheFailed(url); |
+ LoadFromNewestCacheFailed(url, response_info); |
} else { |
DCHECK(group_->newest_complete_cache()); |
AppCacheEntry* copy_me = group_->newest_complete_cache()->GetEntry(url); |
@@ -1204,12 +1224,13 @@ |
MaybeCompleteUpdate(); |
} |
-void AppCacheUpdateJob::LoadFromNewestCacheFailed(const GURL& url) { |
+void AppCacheUpdateJob::LoadFromNewestCacheFailed( |
+ const GURL& url, AppCacheResponseInfo* response_info) { |
if (internal_state_ == CACHE_FAILURE) |
return; |
// Re-insert url at front of fetch list. Indicate storage has been checked. |
- urls_to_fetch_.push_front(AppCacheUpdateJob::UrlsToFetch(url, true)); |
+ urls_to_fetch_.push_front(UrlToFetch(url, true, response_info)); |
FetchUrls(); |
} |