Changed the update protocol for component updater from v2 to v3.
BUG=314521
Review URL: https://codereview.chromium.org/74893002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236727 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/component_updater/component_updater_service.cc b/chrome/browser/component_updater/component_updater_service.cc
index 472525b..beee14a 100644
--- a/chrome/browser/component_updater/component_updater_service.cc
+++ b/chrome/browser/component_updater/component_updater_service.cc
@@ -27,9 +27,10 @@
#include "chrome/browser/component_updater/component_patcher.h"
#include "chrome/browser/component_updater/component_unpacker.h"
#include "chrome/browser/component_updater/component_updater_ping_manager.h"
+#include "chrome/browser/component_updater/component_updater_utils.h"
#include "chrome/browser/component_updater/crx_update_item.h"
+#include "chrome/browser/component_updater/update_manifest.h"
#include "chrome/common/chrome_version_info.h"
-#include "chrome/common/extensions/update_manifest.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/resource_controller.h"
#include "content/public/browser/resource_throttle.h"
@@ -44,57 +45,18 @@
#include "url/gurl.h"
using content::BrowserThread;
-using extensions::Extension;
// The component updater is designed to live until process shutdown, so
// base::Bind() calls are not refcounted.
namespace {
-// 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,
- bool ondemand,
- size_t limit,
- std::string* query) {
- std::string additional =
- base::StringPrintf("id=%s&v=%s&fp=%s&uc%s",
- id.c_str(),
- version.c_str(),
- fingerprint.c_str(),
- ondemand ? "&installsource=ondemand" : "");
- additional = "x=" + net::EscapeQueryParamValue(additional, true);
- if ((additional.size() + query->size() + 1) > limit)
- return false;
- if (!query->empty())
- query->append(1, '&');
- query->append(additional);
- return true;
-}
-
-// Create the final omaha compatible query. The |extra| is optional and can
-// be null. It should contain top level (non-escaped) parameters.
-std::string MakeFinalQuery(const std::string& host,
- const std::string& query,
- const char* extra) {
- std::string request(host);
- request.append(1, '?');
- if (extra) {
- request.append(extra);
- request.append(1, '&');
- }
- request.append(query);
- return request;
-}
-
// Produces an extension-like friendly |id|. This might be removed in the
// future if we roll our on packing tools.
static std::string HexStringToID(const std::string& hexstr) {
std::string id;
for (size_t i = 0; i < hexstr.size(); ++i) {
- int val;
+ int val(0);
if (base::HexStringToInt(base::StringPiece(hexstr.begin() + i,
hexstr.begin() + i + 1),
&val)) {
@@ -103,16 +65,14 @@
id.append(1, 'a');
}
}
- DCHECK(Extension::IdIsValid(id));
+ DCHECK(extensions::Extension::IdIsValid(id));
return id;
}
-// Helper to do version check for components.
+// Returns true if the |proposed| version is newer than |current| version.
bool IsVersionNewer(const Version& current, const std::string& proposed) {
Version proposed_ver(proposed);
- if (!proposed_ver.IsValid())
- return false;
- return (current.CompareTo(proposed_ver) < 0);
+ return proposed_ver.IsValid() && current.CompareTo(proposed_ver) < 0;
}
// Helper template class that allows our main class to have separate
@@ -250,6 +210,7 @@
public:
explicit CUResourceThrottle(const net::URLRequest* request);
virtual ~CUResourceThrottle();
+
// Overriden from ResourceThrottle.
virtual void WillStartRequest(bool* defer) OVERRIDE;
virtual void WillRedirectRequest(const GURL& new_url, bool* defer) OVERRIDE;
@@ -260,13 +221,13 @@
typedef std::vector<base::WeakPtr<CUResourceThrottle> > WeakPtrVector;
private:
- enum State {
- NEW,
- BLOCKED,
- UNBLOCKED
- };
+ enum State {
+ NEW,
+ BLOCKED,
+ UNBLOCKED
+ };
- State state_;
+ State state_;
};
void UnblockResourceThrottle(base::WeakPtr<CUResourceThrottle> rt) {
@@ -299,7 +260,6 @@
class CrxUpdateService : public ComponentUpdateService {
public:
explicit CrxUpdateService(ComponentUpdateService::Configurator* config);
-
virtual ~CrxUpdateService();
// Overrides for ComponentUpdateService.
@@ -347,13 +307,10 @@
kStepDelayLong,
};
-
- void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& results);
-
+ void OnParseUpdateManifestSucceeded(
+ const component_updater::UpdateManifest::Results& results);
void OnParseUpdateManifestFailed(const std::string& error_message);
- bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
-
Status OnDemandUpdateInternal(CrxUpdateItem* item);
void ProcessPendingItems();
@@ -362,9 +319,12 @@
void UpdateComponent(CrxUpdateItem* workitem);
- void AddUpdateCheckItems(std::string* query);
+ void AddItemToUpdateCheck(CrxUpdateItem* item,
+ std::string* update_check_items);
- void DoUpdateCheck(const std::string& query);
+ void AddUpdateCheckItems(std::string* update_check_items);
+
+ void DoUpdateCheck(const std::string& update_check_items);
void ScheduleNextRun(StepDelayInterval step_delay);
@@ -636,36 +596,6 @@
return kOk;
}
-// Sets a component to be checked for updates.
-// The component to add is |item| and the |query| string is modified with the
-// required omaha compatible query. Returns false when the query string is
-// longer than specified by UrlSizeLimit().
-bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
- std::string* query) {
- if (!AddQueryString(item->id,
- item->component.version.GetString(),
- item->component.fingerprint,
- item->on_demand,
- config_->UrlSizeLimit(),
- query))
- return false;
-
- ChangeItemState(item, CrxUpdateItem::kChecking);
- item->last_check = base::Time::Now();
- item->previous_version = item->component.version;
- item->next_version = Version();
- item->previous_fp = item->component.fingerprint;
- item->next_fp.clear();
- item->diff_update_failed = false;
- item->error_category = 0;
- item->error_code = 0;
- item->extra_code1 = 0;
- item->diff_error_category = 0;
- item->diff_error_code = 0;
- item->diff_extra_code1 = 0;
- return true;
-}
-
// Start the process of checking for an update, for a particular component
// that was previously registered.
// |component_id| is a value returned from GetCrxComponentID().
@@ -678,6 +608,7 @@
CrxUpdateItem* uit) {
if (!uit)
return kError;
+
// Check if the request is too soon.
base::TimeDelta delta = base::Time::Now() - uit->last_check;
if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay()))
@@ -739,10 +670,10 @@
UpdateComponent(ready_upgrade);
return;
}
- std::string query;
- AddUpdateCheckItems(&query);
- if (!query.empty()) {
- DoUpdateCheck(query);
+ std::string update_check_items;
+ AddUpdateCheckItems(&update_check_items);
+ if (!update_check_items.empty()) {
+ DoUpdateCheck(update_check_items);
return;
}
// No components to update. The next check will be after a long sleep.
@@ -793,16 +724,75 @@
blocking_task_runner_);
}
-// Given that our |work_items_| list is expected to contain relatively few
-// items, we simply loop several times.
-void CrxUpdateService::AddUpdateCheckItems(std::string* query){
+// Sets the state of the component to be checked for updates. After the
+// function is called, the <app> element corresponding to the |item| parameter
+// is appended to the |update_check_items|.
+void CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
+ std::string* update_check_items) {
+ // The app element corresponding to an update items looks like this:
+ // <app appid="hnimpnehoodheedghdeeijklkeaacbdc"
+ // version="0.1.2.3" installsource="ondemand">
+ // <updatecheck />
+ // <packages>
+ // <package fp="abcd" />
+ // </packages>
+ // </app>
+ std::string app_attributes;
+ base::StringAppendF(&app_attributes,
+ "appid=\"%s\" version=\"%s\"",
+ item->id.c_str(),
+ item->component.version.GetString().c_str());
+ if (item->on_demand)
+ base::StringAppendF(&app_attributes, " installsource=\"ondemand\"");
+
+ std::string app;
+ if (item->component.fingerprint.empty())
+ base::StringAppendF(&app,
+ "<app %s>"
+ "<updatecheck />"
+ "</app>",
+ app_attributes.c_str());
+ else
+ base::StringAppendF(&app,
+ "<app %s>"
+ "<updatecheck />"
+ "<packages>"
+ "<package fp=\"%s\"/>"
+ "</packages>"
+ "</app>",
+ app_attributes.c_str(),
+ item->component.fingerprint.c_str());
+
+ update_check_items->append(app);
+
+ ChangeItemState(item, CrxUpdateItem::kChecking);
+ item->last_check = base::Time::Now();
+ item->crx_url = GURL();
+ item->diff_crx_url = GURL();
+ item->previous_version = item->component.version;
+ item->next_version = Version();
+ item->previous_fp = item->component.fingerprint;
+ item->next_fp.clear();
+ item->diff_update_failed = false;
+ item->error_category = 0;
+ item->error_code = 0;
+ item->extra_code1 = 0;
+ item->diff_error_category = 0;
+ item->diff_error_code = 0;
+ item->diff_extra_code1 = 0;
+}
+
+// Builds the sequence of <app> elements in the update check and returns it
+// in the |update_check_items| parameter.
+void CrxUpdateService::AddUpdateCheckItems(std::string* update_check_items) {
+ // Given that our |work_items_| list is expected to contain relatively few
+ // items, we simply loop several times.
for (UpdateItems::const_iterator it = work_items_.begin();
it != work_items_.end(); ++it) {
CrxUpdateItem* item = *it;
if (item->status != CrxUpdateItem::kNew)
continue;
- if (!AddItemToUpdateCheck(item, query))
- break;
+ AddItemToUpdateCheck(item, update_check_items);
}
// Next we can go back to components we already checked, here
@@ -820,8 +810,7 @@
base::TimeDelta delta = base::Time::Now() - item->last_check;
if (delta < min_delta_time)
continue;
- if (!AddItemToUpdateCheck(item, query))
- break;
+ AddItemToUpdateCheck(item, update_check_items);
}
// Finally, we check components that we already updated as long as
@@ -834,24 +823,19 @@
base::TimeDelta delta = base::Time::Now() - item->last_check;
if (delta < min_delta_time)
continue;
- if (!AddItemToUpdateCheck(item, query))
- break;
+ AddItemToUpdateCheck(item, update_check_items);
}
}
-void CrxUpdateService::DoUpdateCheck(const std::string& query) {
- const std::string full_query =
- MakeFinalQuery(config_->UpdateUrl().spec(),
- query,
- config_->ExtraRequestParams());
-
- url_fetcher_.reset(net::URLFetcher::Create(
- 0, GURL(full_query), net::URLFetcher::GET,
- MakeContextDelegate(this, new UpdateContext())));
- StartFetch(url_fetcher_.get(),
- config_->RequestContext(),
- false,
- blocking_task_runner_);
+// Sends an update request. The |update_check_items| parameter
+// contains the sequence of <app> xml elements of the update check.
+void CrxUpdateService::DoUpdateCheck(const std::string& update_check_items) {
+ using component_updater::BuildProtocolRequest;
+ url_fetcher_.reset(component_updater::SendProtocolRequest(
+ config_->UpdateUrl(),
+ BuildProtocolRequest(update_check_items),
+ MakeContextDelegate(this, new UpdateContext()),
+ config_->RequestContext()));
}
// Called when we got a response from the update server. It consists of an xml
@@ -871,16 +855,13 @@
delete context;
}
-// Parsing the manifest is either done right now for tests or in a sandboxed
-// process for the production environment. This mitigates the case where an
-// attacker was able to feed us a malicious xml string.
void CrxUpdateService::ParseManifest(const std::string& xml) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- UpdateManifest manifest;
- if (!manifest.Parse(xml))
- CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
+ component_updater::UpdateManifest manifest;
+ if (manifest.Parse(xml))
+ CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
else
- CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
+ CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
}
// A valid Omaha update check has arrived, from only the list of components that
@@ -888,52 +869,72 @@
// version is newer, if so we queue them for an upgrade. The next time we call
// ProcessPendingItems() one of them will be drafted for the upgrade process.
void CrxUpdateService::OnParseUpdateManifestSucceeded(
- const UpdateManifest::Results& results) {
+ const component_updater::UpdateManifest::Results& results) {
+ size_t num_updates_pending = 0;
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- size_t update_pending = 0;
- std::vector<UpdateManifest::Result>::const_iterator it;
+ std::vector<component_updater::UpdateManifest::Result>::const_iterator it;
for (it = results.list.begin(); it != results.list.end(); ++it) {
CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
if (!crx)
continue;
- if (crx->status != CrxUpdateItem::kChecking)
+ if (crx->status != CrxUpdateItem::kChecking) {
+ NOTREACHED();
continue; // Not updating this component now.
+ }
- if (it->version.empty()) {
+ if (it->manifest.version.empty()) {
// No version means no update available.
ChangeItemState(crx, CrxUpdateItem::kNoUpdate);
continue;
}
- if (!IsVersionNewer(crx->component.version, it->version)) {
- // Our component is up to date.
+
+ if (!IsVersionNewer(crx->component.version, it->manifest.version)) {
+ // The component is up to date.
ChangeItemState(crx, CrxUpdateItem::kUpToDate);
continue;
}
- if (!it->browser_min_version.empty()) {
- if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
- // Does not apply for this chrome version.
+
+ if (!it->manifest.browser_min_version.empty()) {
+ if (IsVersionNewer(chrome_version_, it->manifest.browser_min_version)) {
+ // The component is not compatible with this Chrome version.
ChangeItemState(crx, CrxUpdateItem::kNoUpdate);
continue;
}
}
- // All test passed. Queue an upgrade for this component and fire the
- // notifications.
- crx->crx_url = it->crx_url;
- crx->diff_crx_url = it->diff_crx_url;
+
+ if (it->manifest.packages.size() != 1) {
+ // Assume one and only one package per component.
+ ChangeItemState(crx, CrxUpdateItem::kNoUpdate);
+ continue;
+ }
+
+ // Parse the members of the result and queue an upgrade for this component.
+ crx->next_version = Version(it->manifest.version);
+
+ typedef component_updater::
+ UpdateManifest::Result::Manifest::Package Package;
+ const Package& package(it->manifest.packages[0]);
+ crx->next_fp = package.fingerprint;
+
+ // Select the first url from the list of urls until support for
+ // fall back urls is implemented.
+ if (!it->crx_urls.empty())
+ crx->crx_url = it->crx_urls[0].Resolve(package.name);
+ if (!it->crx_diffurls.empty())
+ crx->diff_crx_url = it->crx_diffurls[0].Resolve(package.namediff);
+
ChangeItemState(crx, CrxUpdateItem::kCanUpdate);
- crx->next_version = Version(it->version);
- crx->next_fp = it->package_fingerprint;
- ++update_pending;
+ ++num_updates_pending;
}
- // All the components that are not mentioned in the manifest we
- // consider them up to date.
+ // All components that are not included in the update response are
+ // considered up to date.
ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
// If there are updates pending we do a short wait, otherwise we take
// a longer delay until we check the components again.
- ScheduleNextRun(update_pending > 0 ? kStepDelayShort : kStepDelayMedium);
+ ScheduleNextRun(num_updates_pending > 0 ? kStepDelayShort : kStepDelayMedium);
}
void CrxUpdateService::OnParseUpdateManifestFailed(
@@ -1164,3 +1165,4 @@
DCHECK(config);
return new CrxUpdateService(config);
}
+