static const size_t kMaxConcurrentUrlFetches = 2;
static const int kMax503Retries = 3;
+static std::string FormatUrlErrorMessage(
+ const char* format, const GURL& url,
+ AppCacheUpdateJob::ResultType error,
+ int response_code) {
+ // Show the net response code if we have one.
+ int code = response_code;
+ if (error != AppCacheUpdateJob::SERVER_ERROR)
+ code = static_cast<int>(error);
+ return base::StringPrintf(format, code, url.spec().c_str());
+}
+
// Helper class for collecting hosts per frontend when sending notifications
// so that only one notification is sent for all hosts using the same frontend.
class HostNotifier {
retry_503_attempts_(0),
buffer_(new net::IOBuffer(kBufferSize)),
request_(job->service_->request_context()
- ->CreateRequest(url, net::DEFAULT_PRIORITY, this)) {}
+ ->CreateRequest(url, net::DEFAULT_PRIORITY, this)),
+ result_(UPDATE_OK) {}
AppCacheUpdateJob::URLFetcher::~URLFetcher() {
}
DCHECK(request_ == request);
// Redirect is not allowed by the update process.
request->Cancel();
+ result_ = REDIRECT_ERROR;
OnResponseCompleted();
}
void AppCacheUpdateJob::URLFetcher::OnResponseStarted(
net::URLRequest *request) {
DCHECK(request == request_);
- if (request->status().is_success() &&
- (request->GetResponseCode() / 100) == 2) {
+ int response_code = -1;
+ if (request->status().is_success())
+ response_code = request->GetResponseCode();
+ if ((response_code / 100) == 2) {
// See http://code.google.com/p/chromium/issues/detail?id=69594
// We willfully violate the HTML5 spec at this point in order
if (request->response_headers()->
HasHeaderValue("cache-control", "no-store")) {
request->Cancel();
+ result_ = SERVER_ERROR; // Not the best match?
OnResponseCompleted();
return;
}
ReadResponseData();
}
} else {
+ if (response_code > 0)
+ result_ = SERVER_ERROR;
+ else
+ result_ = NETWORK_ERROR;
OnResponseCompleted();
}
}
void AppCacheUpdateJob::URLFetcher::OnWriteComplete(int result) {
if (result < 0) {
request_->Cancel();
+ result_ = DISKCACHE_ERROR;
OnResponseCompleted();
return;
}
return writer;
}
-void AppCacheUpdateJob::HandleCacheFailure(const std::string& error_message) {
+void AppCacheUpdateJob::HandleCacheFailure(
+ const std::string& error_message,
+ ResultType result) {
// 6.9.4 cache failure steps 2-8.
DCHECK(internal_state_ != CACHE_FAILURE);
DCHECK(!error_message.empty());
+ DCHECK(result != UPDATE_OK);
internal_state_ = CACHE_FAILURE;
CancelAllUrlFetches();
CancelAllMasterEntryFetches(error_message);
NotifyAllError(error_message);
DiscardInprogressCache();
internal_state_ = COMPLETED;
+ AppCacheHistograms::CountUpdateJobResult(
+ result, manifest_url_.GetOrigin());
DeleteSoon(); // To unwind the stack prior to deletion.
}
storage_->MakeGroupObsolete(group_, this); // async
} else {
const char* kFormatString = "Manifest fetch failed (%d) %s";
- std::string message = base::StringPrintf(kFormatString, response_code,
- manifest_url_.spec().c_str());
- HandleCacheFailure(message);
+ std::string message = FormatUrlErrorMessage(
+ kFormatString, manifest_url_, fetcher->result(), response_code);
+ HandleCacheFailure(message, fetcher->result());
}
}
MaybeCompleteUpdate();
} else {
// Treat failure to mark group obsolete as a cache failure.
- HandleCacheFailure("Failed to mark the cache as obsolete");
+ HandleCacheFailure("Failed to mark the cache as obsolete", DB_ERROR);
}
}
const char* kFormatString = "Failed to parse manifest %s";
const std::string message = base::StringPrintf(kFormatString,
manifest_url_.spec().c_str());
- HandleCacheFailure(message);
+ HandleCacheFailure(message, MANIFEST_ERROR);
VLOG(1) << message;
return;
}
inprogress_cache_->AddOrModifyEntry(url, entry);
} else {
const char* kFormatString = "Resource fetch failed (%d) %s";
- const std::string message = base::StringPrintf(kFormatString,
- response_code, url.spec().c_str());
- HandleCacheFailure(message);
+ std::string message = FormatUrlErrorMessage(
+ kFormatString, url, fetcher->result(), response_code);
+ HandleCacheFailure(message, fetcher->result());
return;
}
} else if (response_code == 404 || response_code == 410) {
}
hosts.clear();
- const char* kFormatString = "Master entry fetch failed (%d) %s";
- const std::string message = base::StringPrintf(kFormatString,
- response_code, request->url().spec().c_str());
+ const char* kFormatString = "Manifest fetch failed (%d) %s";
+ std::string message = FormatUrlErrorMessage(
+ kFormatString, request->url(), fetcher->result(), response_code);
host_notifier.SendErrorNotifications(message);
// In downloading case, update result is different if all master entries
// Section 6.9.4, step 22.3.
if (update_type_ == CACHE_ATTEMPT && pending_master_entries_.empty()) {
- HandleCacheFailure(message);
+ HandleCacheFailure(message, fetcher->result());
return;
}
}
<< " error: " << request->status().error()
<< " response code: " << response_code;
ScheduleUpdateRetry(kRerunDelayMs);
- HandleCacheFailure("Manifest changed during update, scheduling retry");
+ if (response_code == 200) {
+ HandleCacheFailure("Manifest changed during update", MANIFEST_ERROR);
+ } else {
+ const char* kFormatString = "Manifest re-fetch failed (%d) %s";
+ std::string message = FormatUrlErrorMessage(
+ kFormatString, manifest_url_, fetcher->result(), response_code);
+ HandleCacheFailure(message, fetcher->result());
+ }
}
}
base::Bind(&AppCacheUpdateJob::OnManifestDataWriteComplete,
base::Unretained(this)));
} else {
- HandleCacheFailure("Failed to write the manifest headers to storage");
+ HandleCacheFailure("Failed to write the manifest headers to storage",
+ DISKCACHE_ERROR);
}
}
duplicate_response_ids_.push_back(entry.response_id());
StoreGroupAndCache();
} else {
- HandleCacheFailure("Failed to write the manifest data to storage");
+ HandleCacheFailure("Failed to write the manifest data to storage",
+ DISKCACHE_ERROR);
}
}
stored_state_ = STORED;
MaybeCompleteUpdate(); // will definitely complete
} else {
+ stored_state_ = UNSTORED;
+
// Restore inprogress_cache_ to get the proper events delivered
// and the proper cleanup to occur.
if (newest_cache != group->newest_complete_cache())
inprogress_cache_ = newest_cache;
+ ResultType result = DB_ERROR;
std::string message("Failed to commit new cache to storage");
- if (would_exceed_quota)
+ if (would_exceed_quota) {
message.append(", would exceed quota");
- HandleCacheFailure(message);
+ result = QUOTA_ERROR;
+ }
+ HandleCacheFailure(message, result);
}
}
if (service_->storage() == storage_) {
// Use a local variable because service_ is reset in HandleCacheFailure.
AppCacheService* service = service_;
- HandleCacheFailure("Manifest entry not found in existing cache");
+ HandleCacheFailure("Manifest entry not found in existing cache",
+ DB_ERROR);
AppCacheHistograms::AddMissingManifestEntrySample();
service->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
}
NotifyAllAssociatedHosts(UPDATE_READY_EVENT);
DiscardDuplicateResponses();
internal_state_ = COMPLETED;
+ AppCacheHistograms::CountUpdateJobResult(
+ UPDATE_OK, manifest_url_.GetOrigin());
break;
case CACHE_FAILURE:
NOTREACHED(); // See HandleCacheFailure
}
void AppCacheUpdateJob::DiscardInprogressCache() {
+ if (stored_state_ == STORING) {
+ // We can make no assumptions about whether the StoreGroupAndCacheTask
+ // actually completed or not. This condition should only be reachable
+ // during shutdown. Free things up and return to do no harm.
+ inprogress_cache_ = NULL;
+ added_master_entries_.clear();
+ return;
+ }
+
storage_->DoomResponses(manifest_url_, stored_response_ids_);
if (!inprogress_cache_.get()) {
// We have to undo the changes we made, if any, to the existing cache.
- for (std::vector<GURL>::iterator iter = added_master_entries_.begin();
- iter != added_master_entries_.end(); ++iter) {
- DCHECK(group_->newest_complete_cache());
- group_->newest_complete_cache()->RemoveEntry(*iter);
+ if (group_ && group_->newest_complete_cache()) {
+ for (std::vector<GURL>::iterator iter = added_master_entries_.begin();
+ iter != added_master_entries_.end(); ++iter) {
+ group_->newest_complete_cache()->RemoveEntry(*iter);
+ }
}
+ added_master_entries_.clear();
return;
}
(*hosts.begin())->AssociateNoCache(GURL());
inprogress_cache_ = NULL;
+ added_master_entries_.clear();
}
void AppCacheUpdateJob::DiscardDuplicateResponses() {