Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4016)

Unified Diff: chrome/browser/component_updater/component_updater_service.cc

Issue 15908002: Differential updates for components. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/component_updater/component_updater_service.cc
===================================================================
--- chrome/browser/component_updater/component_updater_service.cc (revision 201835)
+++ chrome/browser/component_updater/component_updater_service.cc (working copy)
@@ -12,6 +12,7 @@
#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/guid.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
@@ -19,13 +20,16 @@
#include "base/stringprintf.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
+#include "base/sys_info.h"
#include "base/timer.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/component_updater/component_patcher.h"
#include "chrome/browser/component_updater/component_unpacker.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_utility_messages.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/omaha_query_params/omaha_query_params.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/utility_process_host.h"
@@ -47,21 +51,26 @@
// base::Bind() calls are not refcounted.
namespace {
+
+typedef ComponentUpdateService::Configurator Config;
+
// Manifest sources, from most important to least important.
const CrxComponent::UrlSource kManifestSources[] = {
CrxComponent::BANDAID,
CrxComponent::CWS_PUBLIC,
- CrxComponent::CWS_SANDBOX
+ CrxComponent::CWS_SANDBOX,
};
// Extends an omaha compatible update check url |query| string. Does
// not mutate the string if it would be longer than |limit| chars.
bool AddQueryString(const std::string& id,
const std::string& version,
+ const std::string& fingerprint,
size_t limit,
std::string* query) {
std::string additional =
- base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
+ base::StringPrintf("id=%s&v=%s&fp=%s&uc",
+ id.c_str(), version.c_str(), fingerprint.c_str());
additional = "x=" + net::EscapeQueryParamValue(additional, true);
if ((additional.size() + query->size() + 1) > limit)
return false;
@@ -163,34 +172,81 @@
fetcher->Start();
}
-// Returs true if the url request of |fetcher| was succesful.
+// Returns true if the url request of |fetcher| was succesful.
bool FetchSuccess(const net::URLFetcher& fetcher) {
return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
(fetcher.GetResponseCode() == 200);
}
+// Returns the error code which occured during the fetch. This function
+// is intended to be called for logging purposes only, since it folds different
+// types of errors to fit them in the returned type. The function returns 0 if
+// the fetch was successful. If errors happen, the function could return a
+// network error, an http response code, or the status of the fetch, if the
+// fetch is pending or canceled.
+int GetFetchError(const net::URLFetcher& fetcher) {
+ if (FetchSuccess(fetcher))
+ return 0;
+
+ const net::URLRequestStatus::Status status(fetcher.GetStatus().status());
+ if (status == net::URLRequestStatus::FAILED)
+ return fetcher.GetStatus().error();
+
+ if (status == net::URLRequestStatus::IO_PENDING ||
+ status == net::URLRequestStatus::CANCELED)
+ return status;
+
+ const int response_code(fetcher.GetResponseCode());
+ if (status == net::URLRequestStatus::SUCCESS && response_code != 200)
+ return response_code;
+
+ return -1;
+}
+
+
// This is the one and only per-item state structure. Designed to be hosted
// in a std::vector or a std::list. The two main members are |component|
// which is supplied by the the component updater client and |status| which
// is modified as the item is processed by the update pipeline. The expected
// transition graph is:
-// error error error
-// +--kNoUpdate<------<-------+------<------+------<------+
-// | | | |
-// V yes | | |
-// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
-// ^ | |
-// | |no |
-// |--kUpToDate<---+ |
-// | success |
-// +--kUpdated<-------------------------------------------+
//
+// kNew
+// |
+// V
+// +----------------------> kChecking -<---------+-----<-------+
+// | | | |
+// | V no | |
+// kNoUpdate [update?] ->---- kUpToDate kUpdated
+// ^ | ^
+// | yes | |
+// | diff=false V |
+// | +-----------> kCanUpdate |
+// | | | |
+// | | V no |
+// | | [differential update?]->----+ |
+// | | | | |
+// | | yes | | |
+// | | error V | |
+// | +---------<- kDownloadingDiff | |
+// | | | | |
+// | | | | |
+// | | error V | |
+// | +---------<- kUpdatingDiff ->--------|-----------+ success
+// | | |
+// | error V |
+// +----------------------------------------- kDownloading |
+// | | |
+// | error V |
+// +------------------------------------------ kUpdating ->----+ success
+//
struct CrxUpdateItem {
enum Status {
kNew,
kChecking,
kCanUpdate,
+ kDownloadingDiff,
kDownloading,
+ kUpdatingDiff,
kUpdating,
kUpdated,
kUpToDate,
@@ -199,14 +255,56 @@
};
Status status;
- GURL crx_url;
std::string id;
+ CrxComponent component;
+
base::Time last_check;
- CrxComponent component;
+
+ // True if the update response includes an update for this component.
+ bool is_update_available;
+
+ // True is a completion ping has been queued for this component. If an update
+ // is available for this component, one completion ping must be sent
+ // after the component has reached either the kNoUpdate or kUpdated states.
+ bool ping_queued;
+
+ // These members are initialized with their corresponding values from the
+ // update server response.
+ GURL crx_url;
+ GURL diff_crx_url;
+ std::string package_hash;
+ std::string diff_package_hash;
+ int size;
+ int diff_size;
+
+ // The from/to version and fingerprint values.
+ Version previous_version;
Version next_version;
+ std::string previous_fp;
+ std::string next_fp;
- CrxUpdateItem() : status(kNew) {}
+ // True if the differential update failed for any reason.
+ bool diff_update_failed;
+ // The error information for full and differential updates.
+ int error_code;
+ int extra_code1;
+ int diff_error_code;
+ int diff_extra_code1;
+
+ CrxUpdateItem()
+ : status(kNew),
+ is_update_available(false),
+ ping_queued(false),
+ size(0),
+ diff_size(0),
+ diff_update_failed(false),
+ error_code(0),
+ extra_code1(0),
+ diff_error_code(0),
+ diff_extra_code1(0) {
+ }
+
// Function object used to find a specific component.
class FindById {
public:
@@ -220,10 +318,21 @@
};
};
+// Returns true if a differential update is available for the update item.
+bool IsDiffUpdateAvailable(const CrxUpdateItem* update_item) {
+ return update_item->diff_crx_url.is_valid();
+}
+
+// Returns true if a differential update is available, it has not failed yet,
+// and the configuration allows it.
+bool CanTryDiffUpdate(const CrxUpdateItem* update_item, const Config& config) {
+ return IsDiffUpdateAvailable(update_item) &&
+ !update_item->diff_update_failed &&
+ config.DeltasEnabled();
+}
+
} // namespace.
-typedef ComponentUpdateService::Configurator Config;
-
CrxComponent::CrxComponent()
: installer(NULL),
source(BANDAID) {
@@ -240,7 +349,7 @@
// rest of the browser, so even if we have many components registered and
// eligible for update, we only do one thing at a time with pauses in between
// the tasks. Also when we do network requests there is only one |url_fetcher_|
-// in flight at at a time.
+// in flight at a time.
// There are no locks in this code, the main structure |work_items_| is mutated
// only from the UI thread. The unpack and installation is done in the file
// thread and the network requests are done in the IO thread and in the file
@@ -299,11 +408,16 @@
UpdateContext() : start(base::Time::Now()) {}
};
+ // Context for a completion ping.
+ struct PingContext {
+ };
+
// Context for a crx download url request. See DelegateWithContext above.
struct CRXContext {
ComponentInstaller* installer;
std::vector<uint8> pk_hash;
std::string id;
+ std::string fingerprint;
CRXContext() : installer(NULL) {}
};
@@ -313,14 +427,16 @@
void OnURLFetchComplete(const net::URLFetcher* source,
CRXContext* context);
+ void OnURLFetchComplete(const net::URLFetcher* source,
+ PingContext* context);
+
private:
// See ManifestParserBridge.
void OnParseUpdateManifestSucceeded(
const UpdateManifest::Results& results);
// See ManifestParserBridge.
- void OnParseUpdateManifestFailed(
- const std::string& error_message);
+ void OnParseUpdateManifestFailed(const std::string& error_message);
bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
@@ -333,19 +449,33 @@
void Install(const CRXContext* context, const base::FilePath& crx_path);
void DoneInstalling(const std::string& component_id,
- ComponentUnpacker::Error error);
+ ComponentUnpacker::Error error,
+ int extended_error);
size_t ChangeItemStatus(CrxUpdateItem::Status from,
CrxUpdateItem::Status to);
CrxUpdateItem* FindUpdateItemById(const std::string& id);
+ void SendPing(const std::string& ping);
+
+ // These functions build a ping request. Building the ping changes the state
+ // of the update item to indicate that a completion ping has been collected
+ // and queued for this item.
+ std::string BuildPing() const;
+ std::string BuildPingApps() const;
+ static std::string BuildPingEvent(CrxUpdateItem* item);
+
scoped_ptr<Config> config_;
+ scoped_ptr<ComponentPatcher> component_patcher_;
+
scoped_ptr<net::URLFetcher> url_fetcher_;
+ scoped_ptr<net::URLFetcher> ping_sender_;
+
+ // A collection of every work item.
typedef std::vector<CrxUpdateItem*> UpdateItems;
- // A collection of every work item.
UpdateItems work_items_;
// A particular set of items from work_items_, which should be checked ASAP.
@@ -353,7 +483,10 @@
base::OneShotTimer<CrxUpdateService> timer_;
- Version chrome_version_;
+ const Version chrome_version_;
+ const std::string prod_id_;
+ const std::string os_type_;
+ const std::string os_version_;
bool running_;
@@ -362,10 +495,14 @@
//////////////////////////////////////////////////////////////////////////////
-CrxUpdateService::CrxUpdateService(
- ComponentUpdateService::Configurator* config)
+CrxUpdateService::CrxUpdateService(ComponentUpdateService::Configurator* config)
: config_(config),
+ component_patcher_(config->CreateComponentPatcher()),
chrome_version_(chrome::VersionInfo().Version()),
+ prod_id_(chrome::OmahaQueryParams::GetProdIdString(
+ chrome::OmahaQueryParams::CHROME)),
+ os_type_(chrome::VersionInfo().OSType()),
+ os_version_(base::SysInfo().OperatingSystemVersion()),
running_(false) {
}
@@ -418,6 +555,13 @@
if (!running_)
return;
+ if (!step_delay && config_->PingsEnabled()) {
+ const std::string ping(BuildPing());
+ if (!ping.empty()) {
+ SendPing(ping);
+ }
+ }
+
// Keep the delay short if in the middle of an update (step_delay),
// or there are new requested_work_items_ that have not been processed yet.
int64 delay = (step_delay || requested_work_items_.size() > 0)
@@ -491,6 +635,7 @@
uit = new CrxUpdateItem;
uit->id.swap(id);
uit->component = component;
+
work_items_.push_back(uit);
// If this is the first component registered we call Start to
// schedule the first timer.
@@ -508,10 +653,23 @@
std::string* query) {
if (!AddQueryString(item->id,
item->component.version.GetString(),
+ item->component.fingerprint,
config_->UrlSizeLimit(), query))
return false;
+
item->status = CrxUpdateItem::kChecking;
item->last_check = base::Time::Now();
+ item->is_update_available = false;
+ item->ping_queued = false;
+ item->previous_version = item->component.version;
+ item->next_version = Version();
+ item->previous_fp = item->component.fingerprint;
+ item->next_fp = "";
+ item->diff_update_failed = false;
+ item->error_code = 0;
+ item->extra_code1 = 0;
+ item->diff_error_code = 0;
+ item->diff_extra_code1 = 0;
return true;
}
@@ -544,7 +702,9 @@
// no point in this call, so return kInProgress.
case CrxUpdateItem::kChecking:
case CrxUpdateItem::kCanUpdate:
+ case CrxUpdateItem::kDownloadingDiff:
case CrxUpdateItem::kDownloading:
+ case CrxUpdateItem::kUpdatingDiff:
case CrxUpdateItem::kUpdating:
return kInProgress;
// Otherwise the item was already checked a while back (or it is new),
@@ -583,13 +743,21 @@
if (item->status != CrxUpdateItem::kCanUpdate)
continue;
// Found component to update, start the process.
- item->status = CrxUpdateItem::kDownloading;
CRXContext* context = new CRXContext;
context->pk_hash = item->component.pk_hash;
context->id = item->id;
context->installer = item->component.installer;
+ context->fingerprint = item->next_fp;
+ GURL package_url;
+ if (CanTryDiffUpdate(item, *config_)) {
+ package_url = item->diff_crx_url;
+ item->status = CrxUpdateItem::kDownloadingDiff;
+ } else {
+ package_url = item->crx_url;
+ item->status = CrxUpdateItem::kDownloading;
+ }
url_fetcher_.reset(net::URLFetcher::Create(
- 0, item->crx_url, net::URLFetcher::GET,
+ 0, package_url, net::URLFetcher::GET,
MakeContextDelegate(this, context)));
StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
return;
@@ -752,9 +920,16 @@
}
// All test passed. Queue an upgrade for this component and fire the
// notifications.
+ crx->is_update_available = true;
crx->crx_url = it->crx_url;
+ crx->package_hash = it->package_hash;
+ crx->size = it->size;
+ crx->diff_crx_url = it->diff_crx_url;
+ crx->diff_package_hash = it->diff_package_hash;
+ crx->diff_size = it->diff_size;
crx->status = CrxUpdateItem::kCanUpdate;
crx->next_version = Version(it->version);
+ crx->next_fp = it->package_fingerprint;
++update_pending;
content::NotificationService::current()->Notify(
@@ -789,19 +964,31 @@
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
int error_code = net::OK;
+ CrxUpdateItem* crx = FindUpdateItemById(context->id);
+
if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
- size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
- CrxUpdateItem::kNoUpdate);
- DCHECK_EQ(count, 1ul);
+ if (crx->status == CrxUpdateItem::kDownloadingDiff) {
+ crx->diff_error_code = GetFetchError(*source);
+ crx->diff_update_failed = true;
+ crx->status = CrxUpdateItem::kCanUpdate;
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&CrxUpdateService::ProcessPendingItems,
+ base::Unretained(this)));
+ return;
+ }
+ crx->error_code = GetFetchError(*source);
+ crx->status = CrxUpdateItem::kNoUpdate;
config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
url_fetcher_.reset();
ScheduleNextRun(false);
} else {
base::FilePath temp_crx_path;
CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
- size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
- CrxUpdateItem::kUpdating);
- DCHECK_EQ(count, 1ul);
+ crx->status = (crx->status == CrxUpdateItem::kDownloadingDiff) ?
+ CrxUpdateItem::kUpdatingDiff : CrxUpdateItem::kUpdating;
+
url_fetcher_.reset();
content::NotificationService::current()->Notify(
@@ -829,8 +1016,11 @@
const base::FilePath& crx_path) {
// This function owns the |crx_path| and the |context| object.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- ComponentUnpacker
- unpacker(context->pk_hash, crx_path, context->installer);
+ ComponentUnpacker unpacker(context->pk_hash,
+ crx_path,
+ context->fingerprint,
+ component_patcher_.get(),
+ context->installer);
if (!file_util::Delete(crx_path, false)) {
NOTREACHED() << crx_path.value();
}
@@ -839,7 +1029,7 @@
BrowserThread::UI,
FROM_HERE,
base::Bind(&CrxUpdateService::DoneInstalling, base::Unretained(this),
- context->id, unpacker.error()),
+ context->id, unpacker.error(), unpacker.extended_error()),
base::TimeDelta::FromMilliseconds(config_->StepDelay()));
delete context;
}
@@ -847,14 +1037,37 @@
// Installation has been completed. Adjust the component status and
// schedule the next check.
void CrxUpdateService::DoneInstalling(const std::string& component_id,
- ComponentUnpacker::Error error) {
+ ComponentUnpacker::Error error,
+ int extra_code) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CrxUpdateItem* item = FindUpdateItemById(component_id);
+ if (item->status == CrxUpdateItem::kUpdatingDiff) {
+ if (error != ComponentUnpacker::kNone) {
+ item->diff_error_code = error;
+ item->diff_extra_code1 = extra_code;
+ item->diff_update_failed = true;
+ item->status = CrxUpdateItem::kCanUpdate;
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&CrxUpdateService::ProcessPendingItems,
+ base::Unretained(this)));
+ return;
+ }
+ }
+
+ if (error != ComponentUnpacker::kNone) {
+ item->error_code = error;
+ item->extra_code1 = extra_code;
+ }
+
item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
CrxUpdateItem::kNoUpdate;
- if (item->status == CrxUpdateItem::kUpdated)
+ if (item->status == CrxUpdateItem::kUpdated) {
item->component.version = item->next_version;
+ item->component.fingerprint = item->next_fp;
+ }
Configurator::Events event;
switch (error) {
@@ -873,6 +1086,114 @@
ScheduleNextRun(false);
}
+void CrxUpdateService::SendPing(const std::string& ping) {
+ VLOG(3) << "Sending ping. " << ping;
+ const std::string ping_url(config_->PingUrl().spec());
+ ping_sender_.reset(net::URLFetcher::Create(
+ 0, GURL(ping_url), net::URLFetcher::POST,
+ MakeContextDelegate(this, new PingContext())));
+ ping_sender_->SetUploadData(std::string("application/xml"), ping);
+ StartFetch(ping_sender_.get(), config_->RequestContext(), false);
+}
+
+void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
+ PingContext* context) {
+ VLOG(3) << "Sending component update ping returned "
+ << FetchSuccess(*source);
+ ping_sender_.reset();
+}
+
+// Builds a ping message for the update items that have completed. Returns
+// an empty string if there are no completed items.
+std::string CrxUpdateService::BuildPing() const {
+ const std::string apps(BuildPingApps());
+ if (apps.empty())
+ return std::string();
+
+ const char response_format[] =
+ "<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" "
+ "protocol=\"2.0\" version=\"%s-%s\" requestid=\"{%s}\"> "
+ "<o:os platform=\"%s\" version=\"%s\"/> "
+ "%s"
+ "</o:gupdate>";
+ const std::string response_envelope(
+ base::StringPrintf(response_format,
+ prod_id_.c_str(),
+ chrome_version_.GetString().c_str(),
+ base::GenerateGUID().c_str(),
+ os_type_.c_str(),
+ os_version_.c_str(),
+ apps.c_str()));
+ return response_envelope;
+}
+
+// Returns a string containing the sequence of app elements inside the
+// ping message.
+std::string CrxUpdateService::BuildPingApps() const {
+ const char app_format[] = "<o:app appid=\"%s\" version=\"%s\">%s</o:app>";
+
+ std::string ping_apps;
+ for (UpdateItems::const_iterator it = work_items_.begin();
+ it != work_items_.end(); ++it) {
+ CrxUpdateItem* item = *it;
+ // Only ping once if an update was available and the item is completed.
+ if (item->is_update_available && !item->ping_queued &&
+ (item->status == CrxUpdateItem::kNoUpdate ||
+ item->status == CrxUpdateItem::kUpdated)) {
+ const std::string version = item->component.version.GetString().c_str();
+ ping_apps += base::StringPrintf(app_format,
+ item->id.c_str(),
+ version.c_str(),
+ BuildPingEvent(item).c_str());
+ }
+ }
+ return ping_apps;
+}
+
+// Returns a string representing one ping event for an update item.
+std::string CrxUpdateService::BuildPingEvent(CrxUpdateItem* item) {
+ DCHECK(item->status == CrxUpdateItem::kNoUpdate ||
+ item->status == CrxUpdateItem::kUpdated);
+ DCHECK(item->is_update_available);
+
+ using base::StringAppendF;
+
+ std::string ping_event("<o:event eventtype=\"3\"");
+ const int event_result = item->status == CrxUpdateItem::kUpdated;
+ StringAppendF(&ping_event, " eventresult=\"%d\"", event_result);
+ StringAppendF(&ping_event, " previousversion=\"%s\"",
+ item->previous_version.GetString().c_str());
+ StringAppendF(&ping_event, " nextversion=\"%s\"",
+ item->next_version.GetString().c_str());
+ if (item->error_code) {
+ StringAppendF(&ping_event, " errorcode=\"%d\"", item->error_code);
+ }
+ if (item->extra_code1) {
+ StringAppendF(&ping_event, " extracode1=\"%d\"", item->extra_code1);
+ }
+ if (IsDiffUpdateAvailable(item)) {
+ StringAppendF(&ping_event, " diffresult=\"%d\"", !item->diff_update_failed);
+ }
+ if (item->diff_error_code) {
+ StringAppendF(&ping_event, " differrorcode=\"%d\"", item->diff_error_code);
+ }
+ if (item->diff_extra_code1) {
+ StringAppendF(&ping_event,
+ " diffextracode1=\"%d\"",
+ item->diff_extra_code1);
+ }
+ if (!item->previous_fp.empty()) {
+ StringAppendF(&ping_event, " previousfp=\"%s\"", item->previous_fp.c_str());
+ }
+ if (!item->next_fp.empty()) {
+ StringAppendF(&ping_event, " nextfp=\"%s\"", item->next_fp.c_str());
+ }
+ StringAppendF(&ping_event, "/>");
+ item->ping_queued = true;
+ return ping_event;
+}
+
+
// The component update factory. Using the component updater as a singleton
// is the job of the browser process.
ComponentUpdateService* ComponentUpdateServiceFactory(

Powered by Google App Engine
This is Rietveld 408576698