diff options
Diffstat (limited to 'webkit/appcache/appcache_update_job.cc')
-rw-r--r-- | webkit/appcache/appcache_update_job.cc | 263 |
1 files changed, 200 insertions, 63 deletions
diff --git a/webkit/appcache/appcache_update_job.cc b/webkit/appcache/appcache_update_job.cc index a65731bf..b1cf778 100644 --- a/webkit/appcache/appcache_update_job.cc +++ b/webkit/appcache/appcache_update_job.cc @@ -20,7 +20,8 @@ static const int kMax503Retries = 3; // Extra info associated with requests for use during response processing. // This info is deleted when the URLRequest is deleted. -struct UpdateJobInfo : public URLRequest::UserData { +class UpdateJobInfo : public URLRequest::UserData { + public: enum RequestType { MANIFEST_FETCH, URL_FETCH, @@ -28,16 +29,40 @@ struct UpdateJobInfo : public URLRequest::UserData { }; explicit UpdateJobInfo(RequestType request_type) - : type(request_type), - buffer(new net::IOBuffer(kBufferSize)), - retry_503_attempts(0) { - } - - RequestType type; - scoped_refptr<net::IOBuffer> buffer; - // TODO(jennb): need storage info to stream response data to storage - - int retry_503_attempts; + : type_(request_type), + buffer_(new net::IOBuffer(kBufferSize)), + retry_503_attempts_(0), + update_job_(NULL), + request_(NULL), + wrote_response_info_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(write_callback_( + this, &UpdateJobInfo::OnWriteComplete)) { + } + + void SetUpResponseWriter(AppCacheResponseWriter* writer, + AppCacheUpdateJob* update, + URLRequest* request) { + DCHECK(!response_writer_.get()); + response_writer_.reset(writer); + update_job_ = update; + request_ = request; + } + + void OnWriteComplete(int result) { + // A completed write may delete the URL request and this object. + update_job_->OnWriteResponseComplete(result, request_, this); + } + + RequestType type_; + scoped_refptr<net::IOBuffer> buffer_; + int retry_503_attempts_; + + // Info needed to write responses to storage and process callbacks. + scoped_ptr<AppCacheResponseWriter> response_writer_; + AppCacheUpdateJob* update_job_; + URLRequest* request_; + bool wrote_response_info_; + net::CompletionCallbackImpl<UpdateJobInfo> write_callback_; }; // Helper class for collecting hosts per frontend when sending notifications @@ -82,7 +107,11 @@ AppCacheUpdateJob::AppCacheUpdateJob(AppCacheService* service, internal_state_(FETCH_MANIFEST), master_entries_completed_(0), url_fetches_completed_(0), - manifest_url_request_(NULL) { + manifest_url_request_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(manifest_info_write_callback_( + this, &AppCacheUpdateJob::OnManifestInfoWriteComplete)), + ALLOW_THIS_IN_INITIALIZER_LIST(manifest_data_write_callback_( + this, &AppCacheUpdateJob::OnManifestDataWriteComplete)) { DCHECK(group_); manifest_url_ = group_->manifest_url(); } @@ -160,13 +189,14 @@ void AppCacheUpdateJob::OnResponseStarted(URLRequest *request) { void AppCacheUpdateJob::ReadResponseData(URLRequest* request) { if (internal_state_ == CACHE_FAILURE || internal_state_ == CANCELLED || - internal_state_ == COMPLETED) + internal_state_ == COMPLETED) { return; + } int bytes_read = 0; UpdateJobInfo* info = static_cast<UpdateJobInfo*>(request->GetUserData(this)); - request->Read(info->buffer, kBufferSize, &bytes_read); + request->Read(info->buffer_, kBufferSize, &bytes_read); OnReadCompleted(request, bytes_read); } @@ -179,7 +209,7 @@ void AppCacheUpdateJob::OnReadCompleted(URLRequest* request, int bytes_read) { data_consumed = ConsumeResponseData(request, info, bytes_read); if (data_consumed) { bytes_read = 0; - while (request->Read(info->buffer, kBufferSize, &bytes_read)) { + while (request->Read(info->buffer_, kBufferSize, &bytes_read)) { if (bytes_read > 0) { data_consumed = ConsumeResponseData(request, info, bytes_read); if (!data_consumed) @@ -198,20 +228,22 @@ void AppCacheUpdateJob::OnReadCompleted(URLRequest* request, int bytes_read) { bool AppCacheUpdateJob::ConsumeResponseData(URLRequest* request, UpdateJobInfo* info, int bytes_read) { - switch (info->type) { + DCHECK_GT(bytes_read, 0); + switch (info->type_) { case UpdateJobInfo::MANIFEST_FETCH: - manifest_data_.append(info->buffer->data(), bytes_read); + manifest_data_.append(info->buffer_->data(), bytes_read); break; case UpdateJobInfo::URL_FETCH: - // TODO(jennb): stream data to storage. will be async so need to wait - // for callback before reading next chunk. - // For now, schedule a task to continue reading to simulate async-ness. - MessageLoop::current()->PostTask(FROM_HERE, - method_factory_.NewRunnableMethod( - &AppCacheUpdateJob::ReadResponseData, request)); - return false; + if (!info->response_writer_.get()) { + info->SetUpResponseWriter( + service_->storage()->CreateResponseWriter(manifest_url_), + this, request); + } + info->response_writer_->WriteData(info->buffer_, bytes_read, + &info->write_callback_); + return false; // wait for async write completion to continue reading case UpdateJobInfo::MANIFEST_REFETCH: - manifest_refetch_data_.append(info->buffer->data(), bytes_read); + manifest_refetch_data_.append(info->buffer_->data(), bytes_read); break; default: NOTREACHED(); @@ -219,6 +251,29 @@ bool AppCacheUpdateJob::ConsumeResponseData(URLRequest* request, return true; } +void AppCacheUpdateJob::OnWriteResponseComplete(int result, + URLRequest* request, + UpdateJobInfo* info) { + DCHECK(internal_state_ == DOWNLOADING); + + if (result < 0) { + request->Cancel(); + OnResponseCompleted(request); + return; + } + + if (!info->wrote_response_info_) { + info->wrote_response_info_ = true; + scoped_refptr<HttpResponseInfoIOBuffer> io_buffer = + new HttpResponseInfoIOBuffer( + new net::HttpResponseInfo(request->response_info())); + info->response_writer_->WriteInfo(io_buffer, &info->write_callback_); + return; + } + + ReadResponseData(request); +} + void AppCacheUpdateJob::OnReceivedRedirect(URLRequest* request, const GURL& new_url, bool* defer_redirect) { @@ -237,7 +292,7 @@ void AppCacheUpdateJob::OnResponseCompleted(URLRequest* request) { UpdateJobInfo* info = static_cast<UpdateJobInfo*>(request->GetUserData(this)); - switch (info->type) { + switch (info->type_) { case UpdateJobInfo::MANIFEST_FETCH: HandleManifestFetchCompleted(request); break; @@ -257,7 +312,7 @@ void AppCacheUpdateJob::OnResponseCompleted(URLRequest* request) { bool AppCacheUpdateJob::RetryRequest(URLRequest* request) { UpdateJobInfo* info = static_cast<UpdateJobInfo*>(request->GetUserData(this)); - if (info->retry_503_attempts >= kMax503Retries) { + if (info->retry_503_attempts_ >= kMax503Retries) { return false; } @@ -266,13 +321,13 @@ bool AppCacheUpdateJob::RetryRequest(URLRequest* request) { const GURL& url = request->original_url(); URLRequest* retry = new URLRequest(url, this); - UpdateJobInfo* retry_info = new UpdateJobInfo(info->type); - retry_info->retry_503_attempts = info->retry_503_attempts + 1; + UpdateJobInfo* retry_info = new UpdateJobInfo(info->type_); + retry_info->retry_503_attempts_ = info->retry_503_attempts_ + 1; retry->SetUserData(this, retry_info); retry->set_context(request->context()); retry->set_load_flags(request->load_flags()); - switch (info->type) { + switch (info->type_) { case UpdateJobInfo::MANIFEST_FETCH: case UpdateJobInfo::MANIFEST_REFETCH: manifest_url_request_ = retry; @@ -307,6 +362,8 @@ void AppCacheUpdateJob::HandleManifestFetchCompleted(URLRequest* request) { int response_code = request->GetResponseCode(); std::string mime_type; request->GetMimeType(&mime_type); + manifest_response_info_.reset( + new net::HttpResponseInfo(request->response_info())); if ((response_code / 100 == 2) && mime_type == kManifestMimeType) { if (update_type_ == UPGRADE_ATTEMPT) @@ -315,20 +372,29 @@ void AppCacheUpdateJob::HandleManifestFetchCompleted(URLRequest* request) { ContinueHandleManifestFetchCompleted(true); } else if (response_code == 304 && update_type_ == UPGRADE_ATTEMPT) { ContinueHandleManifestFetchCompleted(false); + } else if (response_code == 404 || response_code == 410) { + service_->storage()->MakeGroupObsolete(group_, this); // async } else { - if (response_code == 404 || response_code == 410) { - group_->set_obsolete(true); - NotifyAllAssociatedHosts(OBSOLETE_EVENT); - NotifyAllPendingMasterHosts(ERROR_EVENT); - internal_state_ = COMPLETED; - } else { - LOG(INFO) << "Cache failure, response code: " << response_code; - internal_state_ = CACHE_FAILURE; - } + LOG(INFO) << "Cache failure, response code: " << response_code; + internal_state_ = CACHE_FAILURE; MaybeCompleteUpdate(); // if not done, run async cache failure steps } } +void AppCacheUpdateJob::OnGroupMadeObsolete(AppCacheGroup* group, + bool success) { + NotifyAllPendingMasterHosts(ERROR_EVENT); + if (success) { + DCHECK(group->is_obsolete()); + NotifyAllAssociatedHosts(OBSOLETE_EVENT); + internal_state_ = COMPLETED; + } else { + // Treat failure to mark group obsolete as a cache failure. + internal_state_ = CACHE_FAILURE; + } + MaybeCompleteUpdate(); +} + void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) { DCHECK(internal_state_ == FETCH_MANIFEST); @@ -382,8 +448,14 @@ void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) { int response_code = request->GetResponseCode(); AppCacheEntry& entry = url_file_list_.find(url)->second; + UpdateJobInfo* info = + static_cast<UpdateJobInfo*>(request->GetUserData(this)); + if (request->status().is_success() && (response_code / 100 == 2)) { - // TODO(jennb): associate storage with the new entry + // Associate storage with the new entry. + DCHECK(info->response_writer_.get()); + entry.set_response_id(info->response_writer_->response_id()); + inprogress_cache_->AddEntry(url, entry); // Foreign entries will be detected during cache selection. @@ -396,7 +468,9 @@ void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) { << " os_error: " << request->status().os_error() << " response code: " << response_code; - // TODO(jennb): discard any stored data for this entry + // TODO(jennb): Discard any stored data for this entry? May be unnecessary + // if handled automatically by storage layer. + if (entry.IsExplicit() || entry.IsFallback()) { internal_state_ = CACHE_FAILURE; @@ -434,35 +508,88 @@ void AppCacheUpdateJob::HandleManifestRefetchCompleted(URLRequest* request) { int response_code = request->GetResponseCode(); if (response_code == 304 || manifest_data_ == manifest_refetch_data_) { - AppCacheEntry entry(AppCacheEntry::MANIFEST); - // TODO(jennb): add manifest_data_ to storage and put storage key in entry - // Also store response headers from request for HTTP cache control. + // Only need to store response in storage if manifest is not already an + // an entry in the cache. + AppCacheEntry* entry = inprogress_cache_->GetEntry(manifest_url_); + if (entry) { + entry->add_types(AppCacheEntry::MANIFEST); + CompleteInprogressCache(); + } else { + manifest_response_writer_.reset( + service_->storage()->CreateResponseWriter(manifest_url_)); + scoped_refptr<HttpResponseInfoIOBuffer> io_buffer = + new HttpResponseInfoIOBuffer(manifest_response_info_.release()); + manifest_response_writer_->WriteInfo(io_buffer, + &manifest_info_write_callback_); + } + } else { + LOG(INFO) << "Request status: " << request->status().status() + << " os_error: " << request->status().os_error() + << " response code: " << response_code; + HandleManifestRefetchFailure(); + } +} + +void AppCacheUpdateJob::OnManifestInfoWriteComplete(int result) { + if (result > 0) { + scoped_refptr<net::StringIOBuffer> io_buffer = + new net::StringIOBuffer(manifest_data_); + manifest_response_writer_->WriteData(io_buffer, manifest_data_.length(), + &manifest_data_write_callback_); + } else { + // Treat storage failure as if refetch of manifest failed. + HandleManifestRefetchFailure(); + } +} + +void AppCacheUpdateJob::OnManifestDataWriteComplete(int result) { + if (result > 0) { + AppCacheEntry entry(AppCacheEntry::MANIFEST, + manifest_response_writer_->response_id()); inprogress_cache_->AddOrModifyEntry(manifest_url_, entry); - inprogress_cache_->set_update_time(base::TimeTicks::Now()); + CompleteInprogressCache(); + } else { + // Treat storage failure as if refetch of manifest failed. + HandleManifestRefetchFailure(); + } +} + +void AppCacheUpdateJob::CompleteInprogressCache() { + inprogress_cache_->set_update_time(base::TimeTicks::Now()); + inprogress_cache_->set_complete(true); - // TODO(jennb): start of part to make async (cache/group storage may fail) - inprogress_cache_->set_complete(true); - group_->AddCache(inprogress_cache_); - protect_new_cache_.swap(inprogress_cache_); + protect_former_newest_cache_ = group_->newest_complete_cache(); + group_->AddCache(inprogress_cache_); + protect_new_cache_.swap(inprogress_cache_); - // TODO(jennb): write new group and cache to storage here + service_->storage()->StoreGroupAndNewestCache(group_, this); // async +} - if (update_type_ == CACHE_ATTEMPT) { +void AppCacheUpdateJob::OnGroupAndNewestCacheStored(AppCacheGroup* group, + bool success) { + if (success) { + if (update_type_ == CACHE_ATTEMPT) NotifyAllAssociatedHosts(CACHED_EVENT); - } else { + else NotifyAllAssociatedHosts(UPDATE_READY_EVENT); - } internal_state_ = COMPLETED; - // TODO(jennb): end of part that needs to be made async. + MaybeCompleteUpdate(); // will definitely complete } else { - LOG(INFO) << "Request status: " << request->status().status() - << " os_error: " << request->status().os_error() - << " response code: " << response_code; - ScheduleUpdateRetry(kRerunDelayMs); - internal_state_ = CACHE_FAILURE; + // TODO(jennb): Change storage so clients won't need to revert group state? + // Change group back to reflect former newest group. + group_->RestoreCacheAsNewest(protect_former_newest_cache_); + protect_new_cache_ = NULL; + + // Treat storage failure as if manifest refetch failed. + HandleManifestRefetchFailure(); } + protect_former_newest_cache_ = NULL; +} - MaybeCompleteUpdate(); // will definitely complete +void AppCacheUpdateJob::HandleManifestRefetchFailure() { + ScheduleUpdateRetry(kRerunDelayMs); + internal_state_ = CACHE_FAILURE; + MaybeCompleteUpdate(); // will definitely complete } void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host, @@ -654,7 +781,7 @@ void AppCacheUpdateJob::CopyEntryToCache(const GURL& url, const AppCacheEntry& src, AppCacheEntry* dest) { DCHECK(dest); - // TODO(jennb): copy storage key from src to dest + dest->set_response_id(src.response_id()); inprogress_cache_->AddEntry(url, *dest); } @@ -718,18 +845,28 @@ void AppCacheUpdateJob::Cancel() { pending_master_entries_.clear(); DiscardInprogressCache(); - // TODO(jennb): cancel any storage callbacks + + // Delete response writer to avoid any callbacks. + if (manifest_response_writer_.get()) + manifest_response_writer_.reset(); + + service_->storage()->CancelDelegateCallbacks(this); } void AppCacheUpdateJob::DiscardInprogressCache() { if (!inprogress_cache_) return; - // TODO(jennb): cleanup stored responses for entries in the cache + // TODO(jennb): Cleanup stored responses for entries in the cache? + // May not be necessary if handled automatically by storage layer. + inprogress_cache_ = NULL; } void AppCacheUpdateJob::DeleteSoon() { + manifest_response_writer_.reset(); + service_->storage()->CancelDelegateCallbacks(this); + // Break the connection with the group so the group cannot call delete // on this object after we've posted a task to delete ourselves. group_->SetUpdateStatus(AppCacheGroup::IDLE); |