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_configurator.cc b/chrome/browser/component_updater/component_updater_configurator.cc
index 8f870a8..c325810 100644
--- a/chrome/browser/component_updater/component_updater_configurator.cc
+++ b/chrome/browser/component_updater/component_updater_configurator.cc
@@ -40,13 +40,10 @@
// Sets the URL for updates.
const char kSwitchUrlSource[] = "url-source";
-// The default url from which an update manifest can be fetched. Can be
+// The default url for the v3 protocol service endpoint. Can be
// overridden with --component-updater=url-source=someurl.
const char kDefaultUrlSource[] =
- "http://clients2.google.com/service/update2/crx";
-
-// The url to send the pings to.
-const char kPingUrl[] = "http://tools.google.com/service/update2";
+ "http://clients2.google.com/service/update2";
#if defined(OS_WIN)
// Disables differential updates.
@@ -179,7 +176,7 @@
}
GURL ChromeConfigurator::PingUrl() {
- return pings_enabled_ ? GURL(kPingUrl) : GURL();
+ return pings_enabled_ ? UpdateUrl() : GURL();
}
const char* ChromeConfigurator::ExtraRequestParams() {
diff --git a/chrome/browser/component_updater/component_updater_ping_manager.cc b/chrome/browser/component_updater/component_updater_ping_manager.cc
index d11f4f34..a5c5a14 100644
--- a/chrome/browser/component_updater/component_updater_ping_manager.cc
+++ b/chrome/browser/component_updater/component_updater_ping_manager.cc
@@ -5,18 +5,13 @@
#include "chrome/browser/component_updater/component_updater_ping_manager.h"
#include "base/guid.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/sys_info.h"
-#include "base/win/windows_version.h"
+#include "chrome/browser/component_updater/component_updater_utils.h"
#include "chrome/browser/component_updater/crx_update_item.h"
-#include "chrome/common/chrome_version_info.h"
-#include "chrome/common/omaha_query_params/omaha_query_params.h"
-#include "net/base/load_flags.h"
-#include "net/base/net_errors.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_status.h"
namespace {
@@ -69,45 +64,26 @@
if (!ping_url.is_valid())
return;
- url_fetcher_.reset(net::URLFetcher::Create(0,
- ping_url,
- net::URLFetcher::POST,
- this));
-
- url_fetcher_->SetUploadData("application/xml", BuildPing(item));
- url_fetcher_->SetRequestContext(url_request_context_getter);
- url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DISABLE_CACHE);
- url_fetcher_->SetAutomaticallyRetryOn5xx(false);
- url_fetcher_->Start();
+ url_fetcher_.reset(SendProtocolRequest(ping_url,
+ BuildPing(item),
+ this,
+ url_request_context_getter));
}
// Builds a ping message for the specified update item.
std::string PingSender::BuildPing(const CrxUpdateItem* item) {
- const std::string prod_id(chrome::OmahaQueryParams::GetProdIdString(
- chrome::OmahaQueryParams::CHROME));
-
- const char request_format[] =
- "<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" "
- "protocol=\"2.0\" version=\"%s-%s\" requestid=\"{%s}\" "
- "updaterchannel=\"%s\"> "
- "<o:os platform=\"%s\" version=\"%s\"/> "
- "<o:app appid=\"%s\" version=\"%s\">"
+ const char app_element_format[] =
+ "<app appid=\"%s\" version=\"%s\" nextversion=\"%s\">"
"%s"
- "</o:app></o:gupdate>";
- const std::string request(
- base::StringPrintf(request_format,
- prod_id.c_str(),
- chrome::VersionInfo().Version().c_str(),
- base::GenerateGUID().c_str(),
- chrome::OmahaQueryParams::GetChannelString(),
- chrome::VersionInfo().OSType().c_str(),
- base::SysInfo().OperatingSystemVersion().c_str(),
- item->id.c_str(),
- item->component.version.GetString().c_str(),
- BuildPingEventElement(item).c_str()));
- return request;
+ "</app>";
+ const std::string app_element(base::StringPrintf(
+ app_element_format,
+ item->id.c_str(), // "appid"
+ item->previous_version.GetString().c_str(), // "version"
+ item->next_version.GetString().c_str(), // "nextversion"
+ BuildPingEventElement(item).c_str()));
+
+ return BuildProtocolRequest(app_element);
}
// Returns a string representing one ping event xml element for an update item.
@@ -117,13 +93,9 @@
using base::StringAppendF;
- std::string ping_event("<o:event eventtype=\"3\"");
+ std::string ping_event("<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_category)
StringAppendF(&ping_event, " errorcat=\"%d\"", item->error_category);
if (item->error_code)
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);
}
+
diff --git a/chrome/browser/component_updater/component_updater_utils.cc b/chrome/browser/component_updater/component_updater_utils.cc
new file mode 100644
index 0000000..377611d
--- /dev/null
+++ b/chrome/browser/component_updater/component_updater_utils.cc
@@ -0,0 +1,73 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/component_updater/component_updater_utils.h"
+#include "base/guid.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
+#include "base/win/windows_version.h"
+#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/omaha_query_params/omaha_query_params.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace component_updater {
+
+std::string BuildProtocolRequest(const std::string& request_body) {
+ const char request_format[] =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<request protocol=\"3.0\" version=\"%s-%s\" prodversion=\"%s\" "
+ "requestid=\"{%s}\" updaterchannel=\"%s\" arch=\"%s\" nacl_arch=\"%s\">"
+ "<os platform=\"%s\" version=\"%s\" arch=\"%s\"/>"
+ "%s"
+ "</request>";
+
+ const std::string prod_id(chrome::OmahaQueryParams::GetProdIdString(
+ chrome::OmahaQueryParams::CHROME));
+ const std::string chrome_version(chrome::VersionInfo().Version().c_str());
+
+ const std::string request(base::StringPrintf(request_format,
+ // Chrome version and platform information.
+ prod_id.c_str(), chrome_version.c_str(), // "version"
+ chrome_version.c_str(), // "prodversion"
+ base::GenerateGUID().c_str(), // "requestid"
+ chrome::OmahaQueryParams::GetChannelString(), // "updaterchannel"
+ chrome::OmahaQueryParams::getArch(), // "arch"
+ chrome::OmahaQueryParams::getNaclArch(), // "nacl_arch"
+
+ // OS version and platform information.
+ chrome::VersionInfo().OSType().c_str(), // "platform"
+ base::SysInfo().OperatingSystemVersion().c_str(), // "version"
+ base::SysInfo().OperatingSystemArchitecture().c_str(), // "arch"
+
+ request_body.c_str())); // The actual payload of the request.
+
+ return request;
+}
+
+net::URLFetcher* SendProtocolRequest(
+ const GURL& url,
+ const std::string& protocol_request,
+ net::URLFetcherDelegate* url_fetcher_delegate,
+ net::URLRequestContextGetter* url_request_context_getter) {
+ net::URLFetcher* url_fetcher(
+ net::URLFetcher::Create(0,
+ url,
+ net::URLFetcher::POST,
+ url_fetcher_delegate));
+
+ url_fetcher->SetUploadData("application/xml", protocol_request);
+ url_fetcher->SetRequestContext(url_request_context_getter);
+ url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DISABLE_CACHE);
+ url_fetcher->SetAutomaticallyRetryOn5xx(false);
+ url_fetcher->Start();
+
+ return url_fetcher;
+}
+
+} // namespace component_updater
+
diff --git a/chrome/browser/component_updater/component_updater_utils.h b/chrome/browser/component_updater/component_updater_utils.h
new file mode 100644
index 0000000..4db61fb
--- /dev/null
+++ b/chrome/browser/component_updater/component_updater_utils.h
@@ -0,0 +1,49 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_UTILS_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_UTILS_H_
+
+#include <string>
+
+class GURL;
+
+namespace net {
+class URLFetcher;
+class URLFetcherDelegate;
+class URLRequestContextGetter;
+}
+
+namespace component_updater {
+
+// An update protocol request starts with a common preamble which includes
+// version and platform information for Chrome and the operating system,
+// followed by a request body, which is the actual payload of the request.
+// For example:
+//
+// <?xml version="1.0" encoding="UTF-8"?>
+// <request protocol="3.0" version="chrome-32.0.1.0" prodversion="32.0.1.0"
+// requestid="{7383396D-B4DD-46E1-9104-AAC6B918E792}"
+// updaterchannel="canary" arch="x86" nacl_arch="x86-64">
+// <os platform="win" version="6.1" arch="x86"/>
+// ... REQUEST BODY ...
+// </request>
+
+// Builds a protocol request string by creating the outer envelope for
+// the request and including the request body specified as a parameter.
+std::string BuildProtocolRequest(const std::string& request_body);
+
+// Sends a protocol request to the the service endpoint specified by |url|.
+// The body of the request is provided by |protocol_request| and it is
+// expected to contain XML data. The caller owns the returned object.
+net::URLFetcher* SendProtocolRequest(
+ const GURL& url,
+ const std::string& protocol_request,
+ net::URLFetcherDelegate* url_fetcher_delegate,
+ net::URLRequestContextGetter* url_request_context_getter);
+
+} // namespace component_updater
+
+#endif // CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_UTILS_H_
+
diff --git a/chrome/browser/component_updater/test/component_updater_service_unittest.cc b/chrome/browser/component_updater/test/component_updater_service_unittest.cc
index 693e3e5..de1adf4 100644
--- a/chrome/browser/component_updater/test/component_updater_service_unittest.cc
+++ b/chrome/browser/component_updater/test/component_updater_service_unittest.cc
@@ -12,7 +12,6 @@
#include "chrome/browser/component_updater/test/test_installer.h"
#include "chrome/common/chrome_paths.h"
#include "content/public/browser/browser_thread.h"
-#include "content/test/net/url_request_prepackaged_interceptor.h"
#include "libxml/globals.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/url_request/url_fetcher.h"
@@ -26,6 +25,10 @@
namespace component_updater {
+#define POST_INTERCEPT_SCHEME "http"
+#define POST_INTERCEPT_HOSTNAME "localhost2"
+#define POST_INTERCEPT_PATH "/update2"
+
MockComponentObserver::MockComponentObserver() {
}
@@ -75,11 +78,12 @@
}
GURL TestConfigurator::UpdateUrl() {
- return GURL("http://localhost/upd");
+ return GURL(POST_INTERCEPT_SCHEME "://"
+ POST_INTERCEPT_HOSTNAME POST_INTERCEPT_PATH);
}
GURL TestConfigurator::PingUrl() {
- return GURL("http://localhost2/update2");
+ return UpdateUrl();
}
const char* TestConfigurator::ExtraRequestParams() { return "extra=foo"; }
@@ -122,12 +126,14 @@
InterceptorFactory::InterceptorFactory()
- : URLRequestPostInterceptorFactory("http", "localhost2") {}
+ : URLRequestPostInterceptorFactory(POST_INTERCEPT_SCHEME,
+ POST_INTERCEPT_HOSTNAME) {}
InterceptorFactory::~InterceptorFactory() {}
URLRequestPostInterceptor* InterceptorFactory::CreateInterceptor() {
- return URLRequestPostInterceptorFactory::CreateInterceptor("/update2");
+ return URLRequestPostInterceptorFactory::CreateInterceptor(
+ base::FilePath::FromUTF8Unsafe(POST_INTERCEPT_PATH));
}
class PartialMatch : public URLRequestPostInterceptor::RequestMatcher {
@@ -163,11 +169,15 @@
}
void ComponentUpdaterTest::SetUp() {
+ get_interceptor_.reset(new GetInterceptor);
interceptor_factory_.reset(new InterceptorFactory);
+ post_interceptor_ = interceptor_factory_->CreateInterceptor();
+ EXPECT_TRUE(post_interceptor_);
}
void ComponentUpdaterTest::TearDown() {
interceptor_factory_.reset();
+ get_interceptor_.reset();
xmlCleanupGlobals();
}
@@ -208,6 +218,12 @@
base::RunLoop runloop;
test_configurator()->SetQuitClosure(runloop.QuitClosure());
runloop.Run();
+
+ // Since some tests need to drain currently enqueued tasks such as network
+ // intercepts on the IO thread, run the threads until they are
+ // idle. The component updater service won't loop again until the loop count
+ // is set and the service is started.
+ RunThreadsUntilIdle();
}
void ComponentUpdaterTest::RunThreadsUntilIdle() {
@@ -237,16 +253,23 @@
// the COMPONENT_UPDATER_STARTED and COMPONENT_UPDATER_SLEEPING notifications
// are generated. No pings are sent.
TEST_F(ComponentUpdaterTest, CheckCrxSleep) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "event eventtype")));
-
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
-
MockComponentObserver observer;
+ EXPECT_CALL(observer,
+ OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+ .Times(1);
+ EXPECT_CALL(observer,
+ OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+ .Times(2);
+ EXPECT_CALL(observer,
+ OnEvent(ComponentObserver::COMPONENT_NOT_UPDATED, 0))
+ .Times(2);
+
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+
TestInstaller installer;
CrxComponent com;
com.observer = &observer;
@@ -256,68 +279,69 @@
Version("1.1"),
&installer));
- const GURL expected_update_url(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D1.1%26fp%3D%26uc");
-
- interceptor.SetResponse(expected_update_url,
- test_file("updatecheck_reply_1.xml"));
-
// We loop twice, but there are no updates so we expect two sleep messages.
test_configurator()->SetLoopCount(2);
-
- EXPECT_CALL(observer,
- OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
- .Times(1);
component_updater()->Start();
-
- EXPECT_CALL(observer,
- OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
- .Times(2);
-
- EXPECT_CALL(observer,
- OnEvent(ComponentObserver::COMPONENT_NOT_UPDATED, 0))
- .Times(2);
RunThreads();
- EXPECT_EQ(2, interceptor.GetHitCount());
-
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count());
+ // Expect to see the two update check requests and no other requests,
+ // including pings.
+ EXPECT_EQ(2, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"1.1\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"1.1\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+
component_updater()->Stop();
// Loop twice again but this case we simulate a server error by returning
- // an empty file.
-
- interceptor.SetResponse(expected_update_url,
- test_file("updatecheck_reply_empty"));
-
- test_configurator()->SetLoopCount(2);
-
+ // an empty file. Expect the behavior of the service to be the same as before.
EXPECT_CALL(observer,
OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
.Times(1);
- component_updater()->Start();
-
EXPECT_CALL(observer,
OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
.Times(2);
EXPECT_CALL(observer,
OnEvent(ComponentObserver::COMPONENT_NOT_UPDATED, 0))
.Times(2);
+
+ post_interceptor_->Reset();
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_empty")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_empty")));
+
+ test_configurator()->SetLoopCount(2);
+ component_updater()->Start();
RunThreads();
- EXPECT_EQ(0, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
-
- EXPECT_EQ(4, interceptor.GetHitCount());
-
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count());
+ EXPECT_EQ(2, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"1.1\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"1.1\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+
component_updater()->Stop();
}
@@ -325,22 +349,12 @@
// the notifications above COMPONENT_UPDATE_FOUND and COMPONENT_UPDATE_READY
// should have been fired. We do two loops so the second time around there
// should be nothing left to do.
-// We also check that only 3 non-ping network requests are issued:
+// We also check that the following network requests are issued:
// 1- manifest check
// 2- download crx
-// 3- second manifest check.
-// Only one ping is sent.
+// 3- ping
+// 4- second manifest check.
TEST_F(ComponentUpdaterTest, InstallCrx) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"1.0\">"
- "<o:event eventtype=\"3\" eventresult=\"1\" "
- "previousversion=\"0.9\" nextversion=\"1.0\"/></o:app>")));
-
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
-
MockComponentObserver observer1;
{
InSequence seq;
@@ -387,6 +401,16 @@
.Times(1);
}
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+
+ get_interceptor_->SetResponse(
+ GURL(expected_crx_url),
+ test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
+
TestInstaller installer1;
CrxComponent com1;
com1.observer = &observer1;
@@ -396,25 +420,7 @@
com2.observer = &observer2;
RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), &installer2);
- const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
-
- const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc");
-
- interceptor.SetResponse(expected_update_url_1,
- test_file("updatecheck_reply_1.xml"));
- interceptor.SetResponse(expected_update_url_2,
- test_file("updatecheck_reply_1.xml"));
- interceptor.SetResponse(GURL(expected_crx_url),
- test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
-
test_configurator()->SetLoopCount(2);
-
component_updater()->Start();
RunThreads();
@@ -423,12 +429,37 @@
EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->install_count());
- EXPECT_EQ(3, interceptor.GetHitCount());
+ // Expect three request in total: two update checks and one ping.
+ EXPECT_EQ(3, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(3, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
+ // Expect one component download.
+ EXPECT_EQ(1, get_interceptor_->GetHitCount());
+
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"2.2\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" "
+ "version=\"0.9\" nextversion=\"1.0\">"
+ "<event eventtype=\"3\" eventresult=\"1\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[2].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"1.0\">"
+ "<updatecheck /></app>"));
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[2].find(
+ "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"2.2\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
component_updater()->Stop();
}
@@ -437,34 +468,29 @@
// particular there should not be an install because the minimum product
// version is much higher than of chrome.
TEST_F(ComponentUpdaterTest, ProdVersionCheck) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "event eventtype")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_2.xml")));
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ get_interceptor_->SetResponse(
+ GURL(expected_crx_url),
+ test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
TestInstaller installer;
CrxComponent com;
RegisterComponent(&com, kTestComponent_jebg, Version("0.9"), &installer);
- const GURL expected_update_url(
- "http://localhost/upd?extra=foo&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
-
- interceptor.SetResponse(expected_update_url,
- test_file("updatecheck_reply_2.xml"));
- interceptor.SetResponse(GURL(expected_crx_url),
- test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
-
test_configurator()->SetLoopCount(1);
component_updater()->Start();
RunThreads();
- EXPECT_EQ(0, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(1, interceptor.GetHitCount());
+ // Expect one update check and no ping.
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+
+ // Expect no download to occur.
+ EXPECT_EQ(0, get_interceptor_->GetHitCount());
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count());
@@ -479,16 +505,6 @@
// - We make an on demand call.
// - This triggers a second loop, which has a reply that triggers an install.
TEST_F(ComponentUpdaterTest, OnDemandUpdate) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"1.0\">"
- "<o:event eventtype=\"3\" eventresult=\"1\" "
- "previousversion=\"0.9\" nextversion=\"1.0\"/></o:app>")));
-
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
-
MockComponentObserver observer1;
{
InSequence seq;
@@ -541,6 +557,13 @@
.Times(1);
}
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_empty")));
+
+ get_interceptor_->SetResponse(
+ GURL(expected_crx_url),
+ test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
+
TestInstaller installer1;
CrxComponent com1;
com1.observer = &observer1;
@@ -550,30 +573,25 @@
com2.observer = &observer2;
RegisterComponent(&com2, kTestComponent_jebg, Version("0.9"), &installer2);
- const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
-
- const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
- "%26installsource%3Dondemand"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
-
- interceptor.SetResponse(expected_update_url_1,
- test_file("updatecheck_reply_empty"));
- interceptor.SetResponse(expected_update_url_2,
- test_file("updatecheck_reply_1.xml"));
- interceptor.SetResponse(GURL(expected_crx_url),
- test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
// No update normally.
test_configurator()->SetLoopCount(1);
component_updater()->Start();
RunThreads();
component_updater()->Stop();
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_EQ(0, get_interceptor_->GetHitCount());
+
// Update after an on-demand check is issued.
+ post_interceptor_->Reset();
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+
EXPECT_EQ(ComponentUpdateService::kOk,
OnDemandTester::OnDemand(component_updater(),
GetCrxComponentID(com2)));
@@ -586,7 +604,28 @@
EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
EXPECT_EQ(1, static_cast<TestInstaller*>(com2.installer)->install_count());
- EXPECT_EQ(3, interceptor.GetHitCount());
+ EXPECT_EQ(2, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_EQ(1, get_interceptor_->GetHitCount());
+
+ // Expect the update check to contain an "ondemand" request for the
+ // second component (com2) and a normal request for the other component.
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"abagagagagagagagagagagagagagagag\" "
+ "version=\"2.2\"><updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" "
+ "version=\"0.9\" installsource=\"ondemand\"><updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" "
+ "version=\"0.9\" nextversion=\"1.0\">"
+ "<event eventtype=\"3\" eventresult=\"1\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
// Also check what happens if previous check too soon.
test_configurator()->SetOnDemandTime(60 * 60);
@@ -626,24 +665,24 @@
.Times(1);
}
- const GURL expected_update_url_3(
- "http://localhost/upd?extra=foo"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
-
// No update: error from no server response
- interceptor.SetResponse(expected_update_url_3,
- test_file("updatecheck_reply_empty"));
+ post_interceptor_->Reset();
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_empty")));
+
test_configurator()->SetLoopCount(1);
component_updater()->Start();
EXPECT_EQ(ComponentUpdateService::kOk,
OnDemandTester::OnDemand(component_updater(),
GetCrxComponentID(com2)));
-
RunThreads();
-
component_updater()->Stop();
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+
// No update: already updated to 1.0 so nothing new
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer1));
{
@@ -672,20 +711,21 @@
.Times(1);
}
- interceptor.SetResponse(expected_update_url_3,
- test_file("updatecheck_reply_1.xml"));
+ post_interceptor_->Reset();
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+
test_configurator()->SetLoopCount(1);
component_updater()->Start();
EXPECT_EQ(ComponentUpdateService::kOk,
OnDemandTester::OnDemand(component_updater(),
GetCrxComponentID(com2)));
-
RunThreads();
- EXPECT_EQ(1, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
component_updater()->Stop();
}
@@ -693,16 +733,6 @@
// Verify that a previously registered component can get re-registered
// with a different version.
TEST_F(ComponentUpdaterTest, CheckReRegistration) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"1.0\">"
- "<o:event eventtype=\"3\" eventresult=\"1\" "
- "previousversion=\"0.9\" nextversion=\"1.0\"/></o:app>")));
-
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
-
MockComponentObserver observer1;
{
InSequence seq;
@@ -749,6 +779,16 @@
.Times(1);
}
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+
+ get_interceptor_->SetResponse(
+ GURL(expected_crx_url),
+ test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
+
TestInstaller installer1;
CrxComponent com1;
com1.observer = &observer1;
@@ -758,28 +798,9 @@
com2.observer = &observer2;
RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), &installer2);
- // Start with 0.9, and update to 1.0
- const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
-
- const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc");
-
- interceptor.SetResponse(expected_update_url_1,
- test_file("updatecheck_reply_1.xml"));
- interceptor.SetResponse(expected_update_url_2,
- test_file("updatecheck_reply_1.xml"));
- interceptor.SetResponse(GURL(expected_crx_url),
- test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
-
- // Loop twice to issue two checks: (1) with original 0.9 version
- // and (2) with the updated 1.0 version.
+ // Loop twice to issue two checks: (1) with original 0.9 version, update to
+ // 1.0, and do the second check (2) with the updated 1.0 version.
test_configurator()->SetLoopCount(2);
-
component_updater()->Start();
RunThreads();
@@ -788,12 +809,23 @@
EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->install_count());
- EXPECT_EQ(1, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
+ EXPECT_EQ(3, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, get_interceptor_->GetHitCount());
- EXPECT_EQ(3, interceptor.GetHitCount());
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" "
+ "version=\"0.9\" nextversion=\"1.0\">"
+ "<event eventtype=\"3\" eventresult=\"1\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[2].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"1.0\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
component_updater()->Stop();
@@ -826,6 +858,10 @@
.Times(1);
}
+ post_interceptor_->Reset();
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+
TestInstaller installer3;
EXPECT_EQ(ComponentUpdateService::kReplaced,
RegisterComponent(&com1,
@@ -833,35 +869,27 @@
Version("2.2"),
&installer3));
- // Check that we send out 2.2 as our version.
- // Interceptor's hit count should go up by 1.
- const GURL expected_update_url_3(
- "http://localhost/upd?extra=foo"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D2.2%26fp%3D%26uc"
- "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
-
- interceptor.SetResponse(expected_update_url_3,
- test_file("updatecheck_reply_1.xml"));
-
// Loop once just to notice the check happening with the re-register version.
test_configurator()->SetLoopCount(1);
component_updater()->Start();
RunThreads();
- EXPECT_EQ(4, interceptor.GetHitCount());
-
- // No additional pings are expected.
- EXPECT_EQ(1, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
-
// We created a new installer, so the counts go back to 0.
EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->error());
EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->install_count());
EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->install_count());
+ // One update check and no additional pings are expected.
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"2.2\">"
+ "<updatecheck /></app>"));
+
component_updater()->Stop();
}
@@ -877,67 +905,63 @@
// There should be two pings, one for each update. The second will bear a
// diffresult=1, while the first will not.
TEST_F(ComponentUpdaterTest, DifferentialUpdate) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"1.0\">"
- "<o:event eventtype=\"3\" eventresult=\"1\" "
- "previousversion=\"0.0\" nextversion=\"1.0\" nextfp=\"1\"/></o:app>")));
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">"
- "<o:event eventtype=\"3\" eventresult=\"1\" "
- "previousversion=\"1.0\" nextversion=\"2.0\" "
- "diffresult=\"1\" previousfp=\"1\" nextfp=\"f22\"/></o:app>")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_diff_reply_1.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_diff_reply_2.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_diff_reply_3.xml")));
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ get_interceptor_->SetResponse(
+ GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"),
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
+ get_interceptor_->SetResponse(
+ GURL("http://localhost/download/"
+ "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"),
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"));
VersionedTestInstaller installer;
CrxComponent com;
RegisterComponent(&com, kTestComponent_ihfo, Version("0.0"), &installer);
- const GURL expected_update_url_0(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D0.0%26fp%3D%26uc");
- const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D1%26uc");
- const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
- const GURL expected_crx_url_1(
- "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
- const GURL expected_crx_url_1_diff_2(
- "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
-
- interceptor.SetResponse(expected_update_url_0,
- test_file("updatecheck_diff_reply_1.xml"));
- interceptor.SetResponse(expected_update_url_1,
- test_file("updatecheck_diff_reply_2.xml"));
- interceptor.SetResponse(expected_update_url_2,
- test_file("updatecheck_diff_reply_3.xml"));
- interceptor.SetResponse(expected_crx_url_1,
- test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
- interceptor.SetResponse(
- expected_crx_url_1_diff_2,
- test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"));
-
test_configurator()->SetLoopCount(3);
-
component_updater()->Start();
RunThreads();
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
EXPECT_EQ(2, static_cast<TestInstaller*>(com.installer)->install_count());
- // One ping has the diffresult=1, the other does not.
- EXPECT_EQ(2, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
+ EXPECT_EQ(5, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(5, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, get_interceptor_->GetHitCount());
- EXPECT_EQ(5, interceptor.GetHitCount());
-
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"0.0\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" "
+ "version=\"0.0\" nextversion=\"1.0\">"
+ "<event eventtype=\"3\" eventresult=\"1\" nextfp=\"1\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[2].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"1.0\">"
+ "<updatecheck /><packages><package fp=\"1\"/></packages></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[3].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" "
+ "version=\"1.0\" nextversion=\"2.0\">"
+ "<event eventtype=\"3\" eventresult=\"1\" diffresult=\"1\" "
+ "previousfp=\"1\" nextfp=\"22\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[4].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">"
+ "<updatecheck /><packages><package fp=\"22\"/></packages></app>"))
+ << post_interceptor_->GetRequestsAsString();
component_updater()->Stop();
}
@@ -952,49 +976,28 @@
// 4- update check (loop 2 - no update available)
// There should be one ping for the first attempted update.
TEST_F(ComponentUpdaterTest, DifferentialUpdateFails) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">"
- "<o:event eventtype=\"3\" eventresult=\"1\" "
- "previousversion=\"1.0\" nextversion=\"2.0\" "
- "diffresult=\"0\" differrorcat=\"2\" differrorcode=\"16\" "
- "nextfp=\"f22\"/></o:app>")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_diff_reply_2.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_diff_reply_3.xml")));
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ get_interceptor_->SetResponse(
+ GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"),
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
+ get_interceptor_->SetResponse(
+ GURL("http://localhost/download/"
+ "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"),
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"));
+ get_interceptor_->SetResponse(
+ GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"),
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"));
TestInstaller installer;
CrxComponent com;
RegisterComponent(&com, kTestComponent_ihfo, Version("1.0"), &installer);
- const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D%26uc");
- const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
- const GURL expected_crx_url_1(
- "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
- const GURL expected_crx_url_1_diff_2(
- "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
- const GURL expected_crx_url_2(
- "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx");
-
- interceptor.SetResponse(expected_update_url_1,
- test_file("updatecheck_diff_reply_2.xml"));
- interceptor.SetResponse(expected_update_url_2,
- test_file("updatecheck_diff_reply_3.xml"));
- interceptor.SetResponse(expected_crx_url_1,
- test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
- interceptor.SetResponse(
- expected_crx_url_1_diff_2,
- test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"));
- interceptor.SetResponse(expected_crx_url_2,
- test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"));
-
test_configurator()->SetLoopCount(2);
-
component_updater()->Start();
RunThreads();
@@ -1002,34 +1005,32 @@
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
EXPECT_EQ(1, static_cast<TestInstaller*>(com.installer)->install_count());
- EXPECT_EQ(1, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
+ EXPECT_EQ(3, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(3, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, get_interceptor_->GetHitCount());
- EXPECT_EQ(4, interceptor.GetHitCount());
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"1.0\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" "
+ "version=\"1.0\" nextversion=\"2.0\">"
+ "<event eventtype=\"3\" eventresult=\"1\" diffresult=\"0\" "
+ "differrorcat=\"2\" differrorcode=\"16\" nextfp=\"22\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[2].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">"
+ "<updatecheck /><packages><package fp=\"22\"/></packages></app>"))
+ << post_interceptor_->GetRequestsAsString();
component_updater()->Stop();
}
// Verify that a failed installation causes an install failure ping.
TEST_F(ComponentUpdaterTest, CheckFailedInstallPing) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">"
- "<o:event eventtype=\"3\" eventresult=\"0\" "
- "previousversion=\"0.9\" nextversion=\"1.0\" "
- "errorcat=\"3\" errorcode=\"9\"/></o:app>")));
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">"
- "<o:event eventtype=\"3\" eventresult=\"0\" "
- "previousversion=\"0.9\" nextversion=\"1.0\" "
- "errorcat=\"3\" errorcode=\"9\"/></o:app>")));
-
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
-
// This test installer reports installation failure.
class : public TestInstaller {
virtual bool Install(const base::DictionaryValue& manifest,
@@ -1040,30 +1041,58 @@
}
} installer;
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_1.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ get_interceptor_->SetResponse(
+ GURL(expected_crx_url),
+ test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
+
+ // Start with 0.9, and attempt update to 1.0.
+ // Loop twice to issue two checks: (1) with original 0.9 version
+ // and (2), which should retry with 0.9.
CrxComponent com;
RegisterComponent(&com, kTestComponent_jebg, Version("0.9"), &installer);
- // Start with 0.9, and attempt update to 1.0
- const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo"
- "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
-
- interceptor.SetResponse(expected_update_url_1,
- test_file("updatecheck_reply_1.xml"));
- interceptor.SetResponse(GURL(expected_crx_url),
- test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
-
- // Loop twice to issue two checks: (1) with original 0.9 version
- // and (2), which should retry with 0.9.
test_configurator()->SetLoopCount(2);
component_updater()->Start();
RunThreads();
+ EXPECT_EQ(4, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, get_interceptor_->GetHitCount());
+
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" "
+ "version=\"0.9\" nextversion=\"1.0\">"
+ "<event eventtype=\"3\" eventresult=\"0\" "
+ "errorcat=\"3\" errorcode=\"9\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[2].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[3].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" "
+ "version=\"0.9\" nextversion=\"1.0\">"
+ "<event eventtype=\"3\" eventresult=\"0\" "
+ "errorcat=\"3\" errorcode=\"9\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+
// Loop once more, but expect no ping because a noupdate response is issued.
// This is necessary to clear out the fire-and-forget ping from the previous
// iteration.
- interceptor.SetResponse(expected_update_url_1,
- test_file("updatecheck_reply_noupdate.xml"));
+ post_interceptor_->Reset();
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_reply_noupdate.xml")));
+
test_configurator()->SetLoopCount(1);
component_updater()->Start();
RunThreads();
@@ -1071,12 +1100,15 @@
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
EXPECT_EQ(2, static_cast<TestInstaller*>(com.installer)->install_count());
- EXPECT_EQ(2, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
- EXPECT_EQ(5, interceptor.GetHitCount());
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
component_updater()->Stop();
}
@@ -1085,74 +1117,73 @@
// ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx contains an incorrect
// patching instruction that should fail.
TEST_F(ComponentUpdaterTest, DifferentialUpdateFailErrorcode) {
- URLRequestPostInterceptor* post_interceptor(
- interceptor_factory_->CreateInterceptor());
- EXPECT_TRUE(post_interceptor != NULL);
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"1.0\">"
- "<o:event eventtype=\"3\" eventresult=\"1\" "
- "previousversion=\"0.0\" nextversion=\"1.0\" nextfp=\"1\"/></o:app>")));
- EXPECT_TRUE(post_interceptor->ExpectRequest(new PartialMatch(
- "<o:app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">"
- "<o:event eventtype=\"3\" eventresult=\"1\" "
- "previousversion=\"1.0\" nextversion=\"2.0\" "
- "diffresult=\"0\" differrorcat=\"2\" "
- "differrorcode=\"14\" diffextracode1=\"305\" "
- "previousfp=\"1\" nextfp=\"f22\"/></o:app>")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_diff_reply_1.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_diff_reply_2.xml")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch(
+ "updatecheck"), test_file("updatecheck_diff_reply_3.xml")));
- content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ get_interceptor_->SetResponse(
+ GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"),
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
+ // This intercept returns a different file than what is specified in the
+ // update check response and requested in the download. The file that is
+ // actually dowloaded contains a patching error, an therefore, an error
+ // is injected at the time of patching.
+ get_interceptor_->SetResponse(
+ GURL("http://localhost/download/"
+ "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"),
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx"));
+ get_interceptor_->SetResponse(
+ GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"),
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"));
VersionedTestInstaller installer;
CrxComponent com;
RegisterComponent(&com, kTestComponent_ihfo, Version("0.0"), &installer);
- const GURL expected_update_url_0(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D0.0%26fp%3D%26uc");
- const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D1%26uc");
- const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo"
- "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
- const GURL expected_crx_url_1(
- "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
- const GURL expected_crx_url_1_diff_2(
- "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
- const GURL expected_crx_url_2(
- "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx");
-
- interceptor.SetResponse(expected_update_url_0,
- test_file("updatecheck_diff_reply_1.xml"));
- interceptor.SetResponse(expected_update_url_1,
- test_file("updatecheck_diff_reply_2.xml"));
- interceptor.SetResponse(expected_update_url_2,
- test_file("updatecheck_diff_reply_3.xml"));
- interceptor.SetResponse(expected_crx_url_1,
- test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
- interceptor.SetResponse(
- expected_crx_url_1_diff_2,
- test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx"));
- interceptor.SetResponse(expected_crx_url_2,
- test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"));
-
- test_configurator()->SetLoopCount(2);
-
+ test_configurator()->SetLoopCount(3);
component_updater()->Start();
RunThreads();
component_updater()->Stop();
- // There may still be pings in the queue.
- RunThreadsUntilIdle();
EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
EXPECT_EQ(2, static_cast<TestInstaller*>(com.installer)->install_count());
- EXPECT_EQ(2, post_interceptor->GetHitCount())
- << post_interceptor->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor->GetMissCount())
- << post_interceptor->GetRequestsAsString();
+ EXPECT_EQ(5, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(5, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(3, get_interceptor_->GetHitCount());
- EXPECT_EQ(5, interceptor.GetHitCount());
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"0.0\">"
+ "<updatecheck /></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" "
+ "version=\"0.0\" nextversion=\"1.0\">"
+ "<event eventtype=\"3\" eventresult=\"1\" nextfp=\"1\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[2].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"1.0\">"
+ "<updatecheck /><packages><package fp=\"1\"/></packages></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[3].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" "
+ "version=\"1.0\" nextversion=\"2.0\">"
+ "<event eventtype=\"3\" eventresult=\"1\" "
+ "diffresult=\"0\" differrorcat=\"2\" "
+ "differrorcode=\"14\" diffextracode1=\"305\" "
+ "previousfp=\"1\" nextfp=\"22\"/></app>"))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_NE(string::npos, post_interceptor_->GetRequests()[4].find(
+ "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">"
+ "<updatecheck /><packages><package fp=\"22\"/></packages></app>"))
+ << post_interceptor_->GetRequestsAsString();
}
} // namespace component_updater
diff --git a/chrome/browser/component_updater/test/component_updater_service_unittest.h b/chrome/browser/component_updater/test/component_updater_service_unittest.h
index 6409c45..d7d4946 100644
--- a/chrome/browser/component_updater/test/component_updater_service_unittest.h
+++ b/chrome/browser/component_updater/test/component_updater_service_unittest.h
@@ -19,6 +19,7 @@
#include "chrome/browser/component_updater/test/component_patcher_mock.h"
#include "chrome/browser/component_updater/test/url_request_post_interceptor.h"
#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/test/net/url_request_prepackaged_interceptor.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -27,7 +28,10 @@
namespace component_updater {
-// Intercepts HTTP POST requests sent to |localhost2|.
+// Intercepts HTTP GET requests sent to "localhost".
+typedef content::URLLocalHostRequestPrepackagedInterceptor GetInterceptor;
+
+// Intercepts HTTP POST requests sent to "localhost2".
class InterceptorFactory : public URLRequestPostInterceptorFactory {
public:
InterceptorFactory();
@@ -148,7 +152,9 @@
void RunThreadsUntilIdle();
scoped_ptr<component_updater::InterceptorFactory> interceptor_factory_;
+ URLRequestPostInterceptor* post_interceptor_; // Owned by the factory.
+ scoped_ptr<GetInterceptor> get_interceptor_;
private:
TestConfigurator* test_config_;
base::FilePath test_data_dir_;
diff --git a/chrome/browser/component_updater/test/update_manifest_unittest.cc b/chrome/browser/component_updater/test/update_manifest_unittest.cc
new file mode 100644
index 0000000..1583c49
--- /dev/null
+++ b/chrome/browser/component_updater/test/update_manifest_unittest.cc
@@ -0,0 +1,281 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_vector.h"
+#include "chrome/browser/component_updater/update_manifest.h"
+#include "libxml/globals.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace component_updater {
+
+const char* kValidXml =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<response protocol='3.0'>"
+" <app appid='12345'>"
+" <updatecheck status='ok'>"
+" <urls>"
+" <url codebase='http://example.com/'/>"
+" <url codebasediff='http://diff.example.com/'/>"
+" </urls>"
+" <manifest version='1.2.3.4' prodversionmin='2.0.143.0'>"
+" <packages>"
+" <package name='extension_1_2_3_4.crx'/>"
+" </packages>"
+" </manifest>"
+" </updatecheck>"
+" </app>"
+"</response>";
+
+const char* valid_xml_with_hash =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<response protocol='3.0'>"
+" <app appid='12345'>"
+" <updatecheck status='ok'>"
+" <urls>"
+" <url codebase='http://example.com/'/>"
+" </urls>"
+" <manifest version='1.2.3.4' prodversionmin='2.0.143.0'>"
+" <packages>"
+" <package name='extension_1_2_3_4.crx' hash_sha256='1234'/>"
+" </packages>"
+" </manifest>"
+" </updatecheck>"
+" </app>"
+"</response>";
+
+const char* valid_xml_with_invalid_sizes =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<response protocol='3.0'>"
+" <app appid='12345'>"
+" <updatecheck status='ok'>"
+" <urls>"
+" <url codebase='http://example.com/'/>"
+" </urls>"
+" <manifest version='1.2.3.4' prodversionmin='2.0.143.0'>"
+" <packages>"
+" <package name='1' size='1234'/>"
+" <package name='2' size='-1234'/>"
+" <package name='3' />"
+" <package name='4' size='-a'/>"
+" <package name='5' size='-123467890123456789'/>"
+" <package name='6' size='123467890123456789'/>"
+" </packages>"
+" </manifest>"
+" </updatecheck>"
+" </app>"
+"</response>";
+
+const char* kMissingAppId =
+"<?xml version='1.0'?>"
+"<response protocol='3.0'>"
+" <app>"
+" <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'"
+" version='1.2.3.4' />"
+" </app>"
+"</response>";
+
+const char* kInvalidCodebase =
+"<?xml version='1.0'?>"
+"<response protocol='3.0'>"
+" <app appid='12345' status='ok'>"
+" <updatecheck codebase='example.com/extension_1.2.3.4.crx'"
+" version='1.2.3.4' />"
+" </app>"
+"</response>";
+
+const char* kMissingVersion =
+"<?xml version='1.0'?>"
+"<response protocol='3.0'>"
+" <app appid='12345' status='ok'>"
+" <updatecheck codebase='http://example.com/extension_1.2.3.4.crx' />"
+" </app>"
+"</response>";
+
+const char* kInvalidVersion =
+"<?xml version='1.0'?>"
+"<response protocol='3.0'>"
+" <app appid='12345' status='ok'>"
+" <updatecheck codebase='http://example.com/extension_1.2.3.4.crx' "
+" version='1.2.3.a'/>"
+" </app>"
+"</response>";
+
+// The v3 version of the protocol is not using namespaces. However, the parser
+// must be able to parse responses that include namespaces.
+const char* kUsesNamespacePrefix =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<g:response xmlns='http://www.google.com/update2/response' protocol='3.0'>"
+" <g:app appid='12345'>"
+" <g:updatecheck status='ok'>"
+" <g:urls>"
+" <g:url codebase='http://example.com/'/>"
+" </g:urls>"
+" <g:manifest version='1.2.3.4' prodversionmin='2.0.143.0'>"
+" <g:packages>"
+" <g:package name='extension_1_2_3_4.crx'/>"
+" </g:packages>"
+" </g:manifest>"
+" </g:updatecheck>"
+" </g:app>"
+"</g:response>";
+
+// Includes unrelated <app> tags from other xml namespaces - this should
+// not cause problems.
+const char* kSimilarTagnames =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<response xmlns:a='http://a' protocol='3.0'>"
+" <a:app appid='12345'>"
+" <updatecheck status='ok'>"
+" <urls>"
+" <url codebase='http://example.com/'/>"
+" </urls>"
+" <manifest version='1.2.3.4' prodversionmin='2.0.143.0'>"
+" <packages>"
+" <package name='extension_1_2_3_4.crx'/>"
+" </packages>"
+" </manifest>"
+" </updatecheck>"
+" </a:app>"
+" <b:app appid='xyz' xmlns:b='http://b'>"
+" <updatecheck status='noupdate'/>"
+" </b:app>"
+"</response>";
+
+// Includes a <daystart> tag.
+const char* kWithDaystart =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<response protocol='3.0'>"
+" <daystart elapsed_seconds='456' />"
+" <app appid='12345'>"
+" <updatecheck status='ok'>"
+" <urls>"
+" <url codebase='http://example.com/'/>"
+" </urls>"
+" <manifest version='1.2.3.4' prodversionmin='2.0.143.0'>"
+" <packages>"
+" <package name='extension_1_2_3_4.crx'/>"
+" </packages>"
+" </manifest>"
+" </updatecheck>"
+" </app>"
+"</response>";
+
+// Indicates no updates available - this should not be a parse error.
+const char* kNoUpdate =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<response protocol='3.0'>"
+" <app appid='12345'>"
+" <updatecheck status='noupdate' />"
+" </app>"
+"</response>";
+
+// Includes two <app> tags, one with an error.
+const char* kTwoAppsOneError =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<response protocol='3.0'>"
+" <app appid='aaaaaaaa' status='error-unknownApplication'>"
+" <updatecheck status='error-unknownapplication'/>"
+" </app>"
+" <app appid='bbbbbbbb'>"
+" <updatecheck status='ok'>"
+" <urls>"
+" <url codebase='http://example.com/'/>"
+" </urls>"
+" <manifest version='1.2.3.4' prodversionmin='2.0.143.0'>"
+" <packages>"
+" <package name='extension_1_2_3_4.crx'/>"
+" </packages>"
+" </manifest>"
+" </updatecheck>"
+" </app>"
+"</response>";
+
+TEST(ComponentUpdaterManifestTest, TestUpdateManifest) {
+ UpdateManifest parser;
+
+ // Test parsing of a number of invalid xml cases
+ EXPECT_FALSE(parser.Parse(std::string()));
+ EXPECT_FALSE(parser.errors().empty());
+
+ EXPECT_TRUE(parser.Parse(kMissingAppId));
+ EXPECT_TRUE(parser.results().list.empty());
+ EXPECT_FALSE(parser.errors().empty());
+
+ EXPECT_TRUE(parser.Parse(kInvalidCodebase));
+ EXPECT_TRUE(parser.results().list.empty());
+ EXPECT_FALSE(parser.errors().empty());
+
+ EXPECT_TRUE(parser.Parse(kMissingVersion));
+ EXPECT_TRUE(parser.results().list.empty());
+ EXPECT_FALSE(parser.errors().empty());
+
+ EXPECT_TRUE(parser.Parse(kInvalidVersion));
+ EXPECT_TRUE(parser.results().list.empty());
+ EXPECT_FALSE(parser.errors().empty());
+
+ // Parse some valid XML, and check that all params came out as expected
+ EXPECT_TRUE(parser.Parse(kValidXml));
+ EXPECT_TRUE(parser.errors().empty());
+ EXPECT_EQ(1u, parser.results().list.size());
+ const UpdateManifest::Result* firstResult = &parser.results().list[0];
+ EXPECT_EQ(1u, firstResult->crx_urls.size());
+ EXPECT_EQ(GURL("http://example.com/"), firstResult->crx_urls[0]);
+ EXPECT_EQ(GURL("http://diff.example.com/"), firstResult->crx_diffurls[0]);
+ EXPECT_EQ("1.2.3.4", firstResult->manifest.version);
+ EXPECT_EQ("2.0.143.0", firstResult->manifest.browser_min_version);
+ EXPECT_EQ(1u, firstResult->manifest.packages.size());
+ EXPECT_EQ("extension_1_2_3_4.crx", firstResult->manifest.packages[0].name);
+
+ // Parse some xml that uses namespace prefixes.
+ EXPECT_TRUE(parser.Parse(kUsesNamespacePrefix));
+ EXPECT_TRUE(parser.errors().empty());
+ EXPECT_TRUE(parser.Parse(kSimilarTagnames));
+ EXPECT_TRUE(parser.errors().empty());
+ xmlCleanupGlobals();
+
+ // Parse xml with hash value
+ EXPECT_TRUE(parser.Parse(valid_xml_with_hash));
+ EXPECT_TRUE(parser.errors().empty());
+ EXPECT_FALSE(parser.results().list.empty());
+ firstResult = &parser.results().list[0];
+ EXPECT_FALSE(firstResult->manifest.packages.empty());
+ EXPECT_EQ("1234", firstResult->manifest.packages[0].hash_sha256);
+
+ // Parse xml with package size value
+ EXPECT_TRUE(parser.Parse(valid_xml_with_invalid_sizes));
+ EXPECT_TRUE(parser.errors().empty());
+ EXPECT_FALSE(parser.results().list.empty());
+ firstResult = &parser.results().list[0];
+ EXPECT_FALSE(firstResult->manifest.packages.empty());
+ EXPECT_EQ(1234, firstResult->manifest.packages[0].size);
+ EXPECT_EQ(-1234, firstResult->manifest.packages[1].size);
+ EXPECT_EQ(0, firstResult->manifest.packages[2].size);
+ EXPECT_EQ(0, firstResult->manifest.packages[3].size);
+ EXPECT_EQ(0, firstResult->manifest.packages[4].size);
+ EXPECT_EQ(0, firstResult->manifest.packages[5].size);
+
+ // Parse xml with a <daystart> element.
+ EXPECT_TRUE(parser.Parse(kWithDaystart));
+ EXPECT_TRUE(parser.errors().empty());
+ EXPECT_FALSE(parser.results().list.empty());
+ EXPECT_EQ(parser.results().daystart_elapsed_seconds, 456);
+
+ // Parse a no-update response.
+ EXPECT_TRUE(parser.Parse(kNoUpdate));
+ EXPECT_TRUE(parser.errors().empty());
+ EXPECT_FALSE(parser.results().list.empty());
+ firstResult = &parser.results().list[0];
+ EXPECT_EQ(firstResult->extension_id, "12345");
+ EXPECT_EQ(firstResult->manifest.version, "");
+
+ // Parse xml with one error and one success <app> tag.
+ EXPECT_TRUE(parser.Parse(kTwoAppsOneError));
+ EXPECT_FALSE(parser.errors().empty());
+ EXPECT_EQ(1u, parser.results().list.size());
+ firstResult = &parser.results().list[0];
+ EXPECT_EQ(firstResult->extension_id, "bbbbbbbb");
+}
+
+} // namespace component_updater
+
diff --git a/chrome/browser/component_updater/test/url_request_post_interceptor.cc b/chrome/browser/component_updater/test/url_request_post_interceptor.cc
index 3a511f1..2f0f9926 100644
--- a/chrome/browser/component_updater/test/url_request_post_interceptor.cc
+++ b/chrome/browser/component_updater/test/url_request_post_interceptor.cc
@@ -18,6 +18,7 @@
namespace component_updater {
+// Returns a canned response.
class URLRequestMockJob : public net::URLRequestSimpleJob {
public:
URLRequestMockJob(net::URLRequest* request,
@@ -27,6 +28,10 @@
response_(response) {}
protected:
+ virtual int GetResponseCode() const OVERRIDE {
+ return 200;
+ }
+
virtual int GetData(std::string* mime_type,
std::string* charset,
std::string* data,
@@ -45,9 +50,14 @@
};
URLRequestPostInterceptor::URLRequestPostInterceptor(const GURL& url)
- : url_(url), hit_count_(0), miss_count_(0) {}
+ : url_(url), hit_count_(0) {}
URLRequestPostInterceptor::~URLRequestPostInterceptor() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ ClearExpectations();
+}
+
+void URLRequestPostInterceptor::ClearExpectations() {
while (!expectations_.empty()) {
Expectation expectation(expectations_.front());
delete expectation.first;
@@ -67,10 +77,9 @@
bool URLRequestPostInterceptor::ExpectRequest(
class RequestMatcher* request_matcher,
- const std::string& filepath) {
+ const base::FilePath& filepath) {
std::string response;
- const base::FilePath path(base::FilePath().AppendASCII(filepath));
- if (filepath.empty() || !base::ReadFileToString(path, &response))
+ if (filepath.empty() || !base::ReadFileToString(filepath, &response))
return false;
expectations_.push(std::make_pair(request_matcher, response));
return true;
@@ -81,9 +90,9 @@
return hit_count_;
}
-int URLRequestPostInterceptor::GetMissCount() const {
+int URLRequestPostInterceptor::GetCount() const {
base::AutoLock auto_lock(interceptor_lock_);
- return requests_.size() - hit_count_;
+ return static_cast<int>(requests_.size());
}
std::vector<std::string>
@@ -106,6 +115,13 @@
return s;
}
+void URLRequestPostInterceptor::Reset() {
+ base::AutoLock auto_lock(interceptor_lock_);
+ hit_count_ = 0;
+ requests_.clear();
+ ClearExpectations();
+}
+
class URLRequestPostInterceptor::Delegate
: public net::URLRequestJobFactory::ProtocolHandler {
@@ -180,13 +196,12 @@
const URLRequestPostInterceptor::Expectation& expectation(
interceptor->expectations_.front());
if (expectation.first->Match(request_body)) {
+ const std::string response(expectation.second);
delete expectation.first;
interceptor->expectations_.pop();
++interceptor->hit_count_;
- return new URLRequestMockJob(request,
- network_delegate,
- expectation.second);
+ return new URLRequestMockJob(request, network_delegate, response);
}
}
@@ -222,11 +237,11 @@
}
URLRequestPostInterceptor* URLRequestPostInterceptorFactory::CreateInterceptor(
- const std::string& file_path) {
+ const base::FilePath& filepath) {
const GURL base_url(base::StringPrintf("%s://%s",
scheme_.c_str(),
hostname_.c_str()));
- GURL absolute_url(base_url.Resolve(file_path));
+ GURL absolute_url(base_url.Resolve(filepath.MaybeAsASCII()));
URLRequestPostInterceptor* interceptor(
new URLRequestPostInterceptor(absolute_url));
bool res = BrowserThread::PostTask(
diff --git a/chrome/browser/component_updater/test/url_request_post_interceptor.h b/chrome/browser/component_updater/test/url_request_post_interceptor.h
index ca0f2d15..c7b4b2a 100644
--- a/chrome/browser/component_updater/test/url_request_post_interceptor.h
+++ b/chrome/browser/component_updater/test/url_request_post_interceptor.h
@@ -11,10 +11,13 @@
#include <utility>
#include <vector>
#include "base/basictypes.h"
-#include "base/path_service.h"
#include "base/synchronization/lock.h"
#include "url/gurl.h"
+namespace base {
+class FilePath;
+}
+
namespace net {
class URLRequest;
}
@@ -45,15 +48,14 @@
// |request_matcher| object. Returns |true| if the expectation was set.
bool ExpectRequest(class RequestMatcher* request_matcher);
bool ExpectRequest(class RequestMatcher* request_matcher,
- const std::string& filepath);
+ const base::FilePath& filepath);
// Returns how many requests have been intercepted and matched by
// an expectation. One expectation can only be matched by one request.
int GetHitCount() const;
- // Returns how many requests have been intercepted but not matched by
- // any expectation.
- int GetMissCount() const;
+ // Returns how many requests in total have been captured by the interceptor.
+ int GetCount() const;
// Returns all requests that have been intercepted, matched or not.
std::vector<std::string> GetRequests() const;
@@ -61,6 +63,9 @@
// Returns all requests as a string for debugging purposes.
std::string GetRequestsAsString() const;
+ // Resets the state of the interceptor so that new expectations can be set.
+ void Reset();
+
class Delegate;
private:
@@ -70,11 +75,11 @@
explicit URLRequestPostInterceptor(const GURL& url);
~URLRequestPostInterceptor();
+ void ClearExpectations();
const GURL url_;
mutable base::Lock interceptor_lock_;
mutable int hit_count_;
- mutable int miss_count_;
mutable std::vector<std::string> requests_;
mutable std::queue<Expectation> expectations_;
@@ -90,7 +95,7 @@
// Creates an interceptor object for the specified url path. Returns NULL
// in case of errors or a valid interceptor object otherwise. The caller
// does not own the returned object.
- URLRequestPostInterceptor* CreateInterceptor(const std::string& file_path);
+ URLRequestPostInterceptor* CreateInterceptor(const base::FilePath& filepath);
private:
const std::string scheme_;
diff --git a/chrome/browser/component_updater/update_manifest.cc b/chrome/browser/component_updater/update_manifest.cc
new file mode 100644
index 0000000..f9e1316
--- /dev/null
+++ b/chrome/browser/component_updater/update_manifest.cc
@@ -0,0 +1,338 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/component_updater/update_manifest.h"
+#include <algorithm>
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/version.h"
+#include "libxml/tree.h"
+#include "third_party/libxml/chromium/libxml_utils.h"
+
+namespace component_updater {
+
+static const char* kExpectedResponseProtocol = "3.0";
+
+UpdateManifest::UpdateManifest() {}
+UpdateManifest::~UpdateManifest() {}
+
+UpdateManifest::Results::Results() : daystart_elapsed_seconds(kNoDaystart) {}
+UpdateManifest::Results::~Results() {}
+
+UpdateManifest::Result::Result() {}
+
+UpdateManifest::Result::~Result() {}
+
+UpdateManifest::Result::Manifest::Manifest() {}
+UpdateManifest::Result::Manifest::~Manifest() {}
+
+UpdateManifest::Result::Manifest::Package::Package() : size(0), sizediff(0) {}
+UpdateManifest::Result::Manifest::Package::~Package() {}
+
+void UpdateManifest::ParseError(const char* details, ...) {
+ va_list args;
+ va_start(args, details);
+
+ if (!errors_.empty()) {
+ errors_ += "\r\n";
+ }
+
+ base::StringAppendV(&errors_, details, args);
+ va_end(args);
+}
+
+// Checks whether a given node's name matches |expected_name|.
+static bool TagNameEquals(const xmlNode* node, const char* expected_name) {
+ return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->name));
+}
+
+// Returns child nodes of |root| with name |name|.
+static std::vector<xmlNode*> GetChildren(xmlNode* root, const char* name) {
+ std::vector<xmlNode*> result;
+ for (xmlNode* child = root->children; child != NULL; child = child->next) {
+ if (!TagNameEquals(child, name)) {
+ continue;
+ }
+ result.push_back(child);
+ }
+ return result;
+}
+
+// Returns the value of a named attribute, or the empty string.
+static std::string GetAttribute(xmlNode* node, const char* attribute_name) {
+ const xmlChar* name = reinterpret_cast<const xmlChar*>(attribute_name);
+ for (xmlAttr* attr = node->properties; attr != NULL; attr = attr->next) {
+ if (!xmlStrcmp(attr->name, name) && attr->children &&
+ attr->children->content) {
+ return std::string(reinterpret_cast<const char*>(
+ attr->children->content));
+ }
+ }
+ return std::string();
+}
+
+// This is used for the xml parser to report errors. This assumes the context
+// is a pointer to a std::string where the error message should be appended.
+static void XmlErrorFunc(void *context, const char *message, ...) {
+ va_list args;
+ va_start(args, message);
+ std::string* error = static_cast<std::string*>(context);
+ base::StringAppendV(error, message, args);
+ va_end(args);
+}
+
+// Utility class for cleaning up the xml document when leaving a scope.
+class ScopedXmlDocument {
+ public:
+ explicit ScopedXmlDocument(xmlDocPtr document) : document_(document) {}
+ ~ScopedXmlDocument() {
+ if (document_)
+ xmlFreeDoc(document_);
+ }
+
+ xmlDocPtr get() {
+ return document_;
+ }
+
+ private:
+ xmlDocPtr document_;
+};
+
+// Parses the <package> tag.
+bool ParsePackageTag(xmlNode* package,
+ UpdateManifest::Result* result,
+ std::string* error) {
+ UpdateManifest::Result::Manifest::Package p;
+ p.name = GetAttribute(package, "name");
+ if (p.name.empty()) {
+ *error = "Missing name for package.";
+ return false;
+ }
+
+ p.namediff = GetAttribute(package, "namediff");
+
+ // package_fingerprint is optional. It identifies the package, preferably
+ // with a modified sha256 hash of the package in hex format.
+ p.fingerprint = GetAttribute(package, "fp");
+
+ p.hash_sha256 = GetAttribute(package, "hash_sha256");
+ int size = 0;
+ if (base::StringToInt(GetAttribute(package, "size"), &size)) {
+ p.size = size;
+ }
+
+ p.hashdiff_sha256 = GetAttribute(package, "hashdiff_sha256");
+ int sizediff = 0;
+ if (base::StringToInt(GetAttribute(package, "sizediff"), &sizediff)) {
+ p.sizediff = sizediff;
+ }
+
+ result->manifest.packages.push_back(p);
+
+ return true;
+}
+
+// Parses the <manifest> tag.
+bool ParseManifestTag(xmlNode* manifest,
+ UpdateManifest::Result* result,
+ std::string* error) {
+ // Get the version.
+ result->manifest.version = GetAttribute(manifest, "version");
+ if (result->manifest.version.empty()) {
+ *error = "Missing version for manifest.";
+ return false;
+ }
+ Version version(result->manifest.version);
+ if (!version.IsValid()) {
+ *error = "Invalid version: '";
+ *error += result->manifest.version;
+ *error += "'.";
+ return false;
+ }
+
+ // Get the minimum browser version (not required).
+ result->manifest.browser_min_version =
+ GetAttribute(manifest, "prodversionmin");
+ if (result->manifest.browser_min_version.length()) {
+ Version browser_min_version(result->manifest.browser_min_version);
+ if (!browser_min_version.IsValid()) {
+ *error = "Invalid prodversionmin: '";
+ *error += result->manifest.browser_min_version;
+ *error += "'.";
+ return false;
+ }
+ }
+
+ // Get the <packages> node.
+ std::vector<xmlNode*> packages = GetChildren(manifest, "packages");
+ if (packages.empty()) {
+ *error = "Missing packages tag on manifest.";
+ return false;
+ }
+
+ // Parse each of the <package> tags.
+ std::vector<xmlNode*> package = GetChildren(packages[0], "package");
+ for (size_t i = 0; i != package.size(); ++i) {
+ if (!ParsePackageTag(package[i], result, error))
+ return false;
+ }
+
+ return true;
+}
+
+// Parses the <urls> tag and its children in the <updatecheck>.
+bool ParseUrlsTag(xmlNode* urls,
+ UpdateManifest::Result* result,
+ std::string* error) {
+ // Get the url nodes.
+ std::vector<xmlNode*> url = GetChildren(urls, "url");
+ if (url.empty()) {
+ *error = "Missing url tags on urls.";
+ return false;
+ }
+
+ // Get the list of urls for full and optionally, for diff updates.
+ // There can only be either a codebase or a codebasediff attribute in a tag.
+ for (size_t i = 0; i != url.size(); ++i) {
+ // Find the url to the crx file.
+ const GURL crx_url(GetAttribute(url[i], "codebase"));
+ if (crx_url.is_valid()) {
+ result->crx_urls.push_back(crx_url);
+ continue;
+ }
+ const GURL crx_diffurl(GetAttribute(url[i], "codebasediff"));
+ if (crx_diffurl.is_valid()) {
+ result->crx_diffurls.push_back(crx_diffurl);
+ continue;
+ }
+ }
+
+ // Expect at least one url for full update.
+ if (result->crx_urls.empty()) {
+ *error = "Missing valid url for full update.";
+ return false;
+ }
+
+ return true;
+}
+
+// Parses the <updatecheck> tag.
+bool ParseUpdateCheckTag(xmlNode* updatecheck,
+ UpdateManifest::Result* result,
+ std::string* error) {
+ if (GetAttribute(updatecheck, "status") == "noupdate") {
+ return true;
+ }
+
+ // Get the <urls> tag.
+ std::vector<xmlNode*> urls = GetChildren(updatecheck, "urls");
+ if (urls.empty()) {
+ *error = "Missing urls on updatecheck.";
+ return false;
+ }
+
+ if (!ParseUrlsTag(urls[0], result, error)) {
+ return false;
+ }
+
+ std::vector<xmlNode*> manifests = GetChildren(updatecheck, "manifest");
+ if (urls.empty()) {
+ *error = "Missing urls on updatecheck.";
+ return false;
+ }
+
+ return ParseManifestTag(manifests[0], result, error);
+}
+
+// Parses a single <app> tag.
+bool ParseAppTag(xmlNode* app,
+ UpdateManifest::Result* result,
+ std::string* error) {
+ // Read the crx id.
+ result->extension_id = GetAttribute(app, "appid");
+ if (result->extension_id.empty()) {
+ *error = "Missing appid on app node";
+ return false;
+ }
+
+ // Get the <updatecheck> tag.
+ std::vector<xmlNode*> updates = GetChildren(app, "updatecheck");
+ if (updates.empty()) {
+ *error = "Missing updatecheck on app.";
+ return false;
+ }
+
+ return ParseUpdateCheckTag(updates[0], result, error);
+}
+
+bool UpdateManifest::Parse(const std::string& manifest_xml) {
+ results_.daystart_elapsed_seconds = kNoDaystart;
+ results_.list.clear();
+ errors_.clear();
+
+ if (manifest_xml.length() < 1) {
+ ParseError("Empty xml");
+ return false;
+ }
+
+ std::string xml_errors;
+ ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
+
+ // Start up the xml parser with the manifest_xml contents.
+ ScopedXmlDocument document(xmlParseDoc(
+ reinterpret_cast<const xmlChar*>(manifest_xml.c_str())));
+ if (!document.get()) {
+ ParseError("%s", xml_errors.c_str());
+ return false;
+ }
+
+ xmlNode* root = xmlDocGetRootElement(document.get());
+ if (!root) {
+ ParseError("Missing root node");
+ return false;
+ }
+
+ if (!TagNameEquals(root, "response")) {
+ ParseError("Missing response tag");
+ return false;
+ }
+
+ // Check for the response "protocol" attribute.
+ if (GetAttribute(root, "protocol") != kExpectedResponseProtocol) {
+ ParseError("Missing/incorrect protocol on response tag "
+ "(expected '%s')", kExpectedResponseProtocol);
+ return false;
+ }
+
+ // Parse the first <daystart> if it is present.
+ std::vector<xmlNode*> daystarts = GetChildren(root, "daystart");
+ if (!daystarts.empty()) {
+ xmlNode* first = daystarts[0];
+ std::string elapsed_seconds = GetAttribute(first, "elapsed_seconds");
+ int parsed_elapsed = kNoDaystart;
+ if (base::StringToInt(elapsed_seconds, &parsed_elapsed)) {
+ results_.daystart_elapsed_seconds = parsed_elapsed;
+ }
+ }
+
+ // Parse each of the <app> tags.
+ std::vector<xmlNode*> apps = GetChildren(root, "app");
+ for (size_t i = 0; i != apps.size(); ++i) {
+ Result result;
+ std::string error;
+ if (ParseAppTag(apps[i], &result, &error)) {
+ results_.list.push_back(result);
+ } else {
+ ParseError("%s", error.c_str());
+ }
+ }
+
+ return true;
+}
+
+} // namespace component_updater
+
diff --git a/chrome/browser/component_updater/update_manifest.h b/chrome/browser/component_updater/update_manifest.h
new file mode 100644
index 0000000..109d60a
--- /dev/null
+++ b/chrome/browser/component_updater/update_manifest.h
@@ -0,0 +1,134 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_UPDATE_MANIFEST_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_UPDATE_MANIFEST_H_
+
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "url/gurl.h"
+
+namespace component_updater {
+
+// Parses responses for the update protocol version 3.
+// (http://code.google.com/p/omaha/wiki/ServerProtocol)
+//
+// An update manifest looks like this:
+//
+// <?xml version="1.0" encoding="UTF-8"?>
+// <response protocol="3.0" server="prod">
+// <daystart elapsed_seconds="56508"/>
+// <app appid="{430FD4D0-B729-4F61-AA34-91526481799D}" status="ok">
+// <updatecheck status="noupdate"/>
+// <ping status="ok"/>
+// </app>
+// <app appid="{D0AB2EBC-931B-4013-9FEB-C9C4C2225C8C}" status="ok">
+// <updatecheck status="ok">
+// <urls>
+// <url codebase="http://host/edgedl/chrome/install/782.112/"
+// <url codebasediff="http://fallback/chrome/diff/782.112/"/>
+// </urls>
+// <manifest version="13.0.782.112" prodversionmin="2.0.143.0">
+// <packages>
+// <package name="component.crx"
+// namediff="diff_1.2.3.4.crx"
+// fp="1.123"
+// hash_sha256="9830b4245c4..." size="23963192"
+// hashdiff_sha256="cfb6caf3d0..." sizediff="101"/>
+// </packages>
+// </manifest>
+// </updatecheck>
+// <ping status="ok"/>
+// </app>
+// </response>
+//
+// The <daystart> tag contains a "elapsed_seconds" attribute which refers to
+// the server's notion of how many seconds it has been since midnight.
+//
+// The "appid" attribute of the <app> tag refers to the unique id of the
+// extension. The "codebase" attribute of the <updatecheck> tag is the url to
+// fetch the updated crx file, and the "prodversionmin" attribute refers to
+// the minimum version of the chrome browser that the update applies to.
+//
+// The diff data members correspond to the differential update package, if
+// a differential update is specified in the response.
+class UpdateManifest {
+ public:
+ // The result of parsing one <app> tag in an xml update check manifest.
+ struct Result {
+ struct Manifest {
+ struct Package {
+ Package();
+ ~Package();
+
+ std::string fingerprint;
+
+ // Attributes for the full update.
+ std::string name;
+ std::string hash_sha256;
+ int size;
+
+ // Attributes for the differential update.
+ std::string namediff;
+ std::string hashdiff_sha256;
+ int sizediff;
+ };
+
+ Manifest();
+ ~Manifest();
+
+ std::string version;
+ std::string browser_min_version;
+ std::vector<Package> packages;
+ };
+
+ Result();
+ ~Result();
+
+ std::string extension_id;
+
+ // The list of fallback urls, for full and diff updates respectively.
+ std::vector<GURL> crx_urls;
+ std::vector<GURL> crx_diffurls;
+
+ Manifest manifest;
+ };
+
+ static const int kNoDaystart = -1;
+ struct Results {
+ Results();
+ ~Results();
+
+ // This will be >= 0, or kNoDaystart if the <daystart> tag was not present.
+ int daystart_elapsed_seconds;
+ std::vector<Result> list;
+ };
+
+ UpdateManifest();
+ ~UpdateManifest();
+
+ // Parses an update manifest xml string into Result data. Returns a bool
+ // indicating success or failure. On success, the results are available by
+ // calling results(). The details for any failures are available by calling
+ // errors().
+ bool Parse(const std::string& manifest_xml);
+
+ const Results& results() const { return results_; }
+ const std::string& errors() const { return errors_; }
+
+ private:
+ Results results_;
+ std::string errors_;
+
+ // Adds parse error details to |errors_| string.
+ void ParseError(const char* details, ...);
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateManifest);
+};
+
+} // namespace component_updater
+
+#endif // CHROME_BROWSER_COMPONENT_UPDATER_UPDATE_MANIFEST_H_
+