diff options
author | sorin@chromium.org <sorin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-04 12:29:23 +0000 |
---|---|---|
committer | sorin@chromium.org <sorin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-04 12:29:23 +0000 |
commit | 93e8e2cc11a6007af1ee5176c637dc3de8699500 (patch) | |
tree | 88035e112f7948ed26c9e0d25ad03f63ce575a3c | |
parent | 7bb855498e47207ee741d2705e6c58aceec7470d (diff) | |
download | chromium_src-93e8e2cc11a6007af1ee5176c637dc3de8699500.zip chromium_src-93e8e2cc11a6007af1ee5176c637dc3de8699500.tar.gz chromium_src-93e8e2cc11a6007af1ee5176c637dc3de8699500.tar.bz2 |
Refactor the component updater update check code into its own class.
The idea here is to decouple the code that does update checks and
speaks the Google Update protocol from the rest of the service.
If we want to secure the update channel using our custom message signing,
then changes will be localized to the UpdateChecker module and not touch
the service code.
Also, this change avoids spilling protocol details and XML formatting in
the service module by encapsulating them in the UpdateChecker class.
BUG=330956
Review URL: https://codereview.chromium.org/119843003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@243029 0039d316-1c4b-4281-b951-d872f2087c98
13 files changed, 606 insertions, 251 deletions
diff --git a/chrome/browser/component_updater/component_updater_ping_manager.cc b/chrome/browser/component_updater/component_updater_ping_manager.cc index c283ed0..f825c34 100644 --- a/chrome/browser/component_updater/component_updater_ping_manager.cc +++ b/chrome/browser/component_updater/component_updater_ping_manager.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "chrome/browser/component_updater/component_updater_ping_manager.h" + +#include "base/compiler_specific.h" #include "base/guid.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" diff --git a/chrome/browser/component_updater/component_updater_ping_manager.h b/chrome/browser/component_updater/component_updater_ping_manager.h index abc92ae..f998b7b 100644 --- a/chrome/browser/component_updater/component_updater_ping_manager.h +++ b/chrome/browser/component_updater/component_updater_ping_manager.h @@ -6,7 +6,6 @@ #define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_PING_MANAGER_H_ #include "base/basictypes.h" -#include "base/compiler_specific.h" #include "url/gurl.h" namespace net { diff --git a/chrome/browser/component_updater/component_updater_service.cc b/chrome/browser/component_updater/component_updater_service.cc index 5325d60..690d91f 100644 --- a/chrome/browser/component_updater/component_updater_service.cc +++ b/chrome/browser/component_updater/component_updater_service.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 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. @@ -17,10 +17,6 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/stl_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" #include "base/threading/sequenced_worker_pool.h" #include "base/timer/timer.h" #include "chrome/browser/browser_process.h" @@ -30,18 +26,12 @@ #include "chrome/browser/component_updater/component_updater_utils.h" #include "chrome/browser/component_updater/crx_downloader.h" #include "chrome/browser/component_updater/crx_update_item.h" +#include "chrome/browser/component_updater/update_checker.h" #include "chrome/browser/component_updater/update_response.h" #include "chrome/common/chrome_version_info.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_controller.h" #include "content/public/browser/resource_throttle.h" -#include "extensions/common/extension.h" -#include "net/base/escape.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.h" #include "url/gurl.h" using content::BrowserThread; @@ -54,56 +44,12 @@ using component_updater::CrxUpdateItem; namespace { -// 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(0); - if (base::HexStringToInt(base::StringPiece(hexstr.begin() + i, - hexstr.begin() + i + 1), - &val)) { - id.append(1, val + 'a'); - } else { - id.append(1, 'a'); - } - } - DCHECK(extensions::Extension::IdIsValid(id)); - return id; -} - // Returns true if the |proposed| version is newer than |current| version. bool IsVersionNewer(const Version& current, const std::string& proposed) { Version proposed_ver(proposed); return proposed_ver.IsValid() && current.CompareTo(proposed_ver) < 0; } -// Helper template class that allows our main class to have separate -// OnURLFetchComplete() callbacks for different types of url requests -// they are differentiated by the |Ctx| type. -template <typename Del, typename Ctx> -class DelegateWithContext : public net::URLFetcherDelegate { - public: - DelegateWithContext(Del* delegate, Ctx* context) - : delegate_(delegate), context_(context) {} - - virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { - delegate_->OnURLFetchComplete(source, context_); - delete this; - } - - private: - ~DelegateWithContext() {} - - Del* delegate_; - Ctx* context_; -}; -// This function creates the right DelegateWithContext using template inference. -template <typename Del, typename Ctx> -net::URLFetcherDelegate* MakeContextDelegate(Del* delegate, Ctx* context) { - return new DelegateWithContext<Del, Ctx>(delegate, context); -} - // Returns true if a differential update is available, it has not failed yet, // and the configuration allows it. bool CanTryDiffUpdate(const CrxUpdateItem* update_item, @@ -144,11 +90,6 @@ CrxComponent::CrxComponent() CrxComponent::~CrxComponent() { } -std::string GetCrxComponentID(const CrxComponent& component) { - return HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0], - component.pk_hash.size()/2))); -} - CrxComponentInfo::CrxComponentInfo() { } @@ -231,23 +172,14 @@ class CrxUpdateService : public ComponentUpdateService { virtual content::ResourceThrottle* GetOnDemandResourceThrottle( net::URLRequest* request, const std::string& crx_id) OVERRIDE; - // Context for a update check url request. See DelegateWithContext above. - struct UpdateContext { - base::Time start; - UpdateContext() : start(base::Time::Now()) {} - }; - - // Context for a crx download url request. See DelegateWithContext above. - struct CRXContext { - ComponentInstaller* installer; - std::vector<uint8> pk_hash; - std::string id; - std::string fingerprint; - CRXContext() : installer(NULL) {} - }; - - void OnURLFetchComplete(const net::URLFetcher* source, - UpdateContext* context); + // Context for a crx download url request. + struct CRXContext { + ComponentInstaller* installer; + std::vector<uint8> pk_hash; + std::string id; + std::string fingerprint; + CRXContext() : installer(NULL) {} + }; private: enum ErrorCategory { @@ -263,9 +195,13 @@ class CrxUpdateService : public ComponentUpdateService { kStepDelayLong, }; - void OnParseUpdateResponseSucceeded( + void UpdateCheckComplete( + int error, + const std::string& error_message, + const component_updater::UpdateResponse::Results& results); + void OnUpdateCheckSucceeded( const component_updater::UpdateResponse::Results& results); - void OnParseUpdateResponseFailed(const std::string& error_message); + void OnUpdateCheckFailed(int error, const std::string& error_message); void DownloadComplete( scoped_ptr<CRXContext> crx_context, @@ -275,16 +211,15 @@ class CrxUpdateService : public ComponentUpdateService { void ProcessPendingItems(); - CrxUpdateItem* FindReadyComponent(); + // Find a component that is ready to update. + CrxUpdateItem* FindReadyComponent() const; - void UpdateComponent(CrxUpdateItem* workitem); - - void AddItemToUpdateCheck(CrxUpdateItem* item, - std::string* update_check_items); + // Prepares the components for an update check and initiates the request. + // Returns true if an update check request has been made. Returns false if + // no update check was needed or an error occured. + bool CheckForUpdates(); - void AddUpdateCheckItems(std::string* update_check_items); - - void DoUpdateCheck(const std::string& update_check_items); + void UpdateComponent(CrxUpdateItem* workitem); void ScheduleNextRun(StepDelayInterval step_delay); @@ -315,11 +250,11 @@ class CrxUpdateService : public ComponentUpdateService { scoped_ptr<ComponentPatcher> component_patcher_; - scoped_ptr<net::URLFetcher> url_fetcher_; + scoped_ptr<component_updater::UpdateChecker> update_checker_; scoped_ptr<component_updater::PingManager> ping_manager_; - scoped_ptr<CrxDownloader> crx_downloader_; + scoped_ptr<component_updater::CrxDownloader> crx_downloader_; // A collection of every work item. typedef std::vector<CrxUpdateItem*> UpdateItems; @@ -405,7 +340,7 @@ bool CrxUpdateService::HasOnDemandItems() const { // components. void CrxUpdateService::ScheduleNextRun(StepDelayInterval step_delay) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(url_fetcher_.get() == NULL); + DCHECK(!update_checker_); CHECK(!timer_.IsRunning()); // It could be the case that Stop() had been called while a url request // or unpacking was in flight, if so we arrive here but |running_| is @@ -451,9 +386,7 @@ CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) { UpdateItems::iterator it = std::find_if(work_items_.begin(), work_items_.end(), finder); - if (it == work_items_.end()) - return NULL; - return (*it); + return it != work_items_.end() ? *it : NULL; } // Changes a component's status, clearing on_demand and firing notifications as @@ -515,18 +448,16 @@ size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from, for (UpdateItems::iterator it = work_items_.begin(); it != work_items_.end(); ++it) { CrxUpdateItem* item = *it; - if (item->status != from) - continue; - ChangeItemState(item, to); - ++count; + if (item->status == from) { + ChangeItemState(item, to); + ++count; + } } return count; } // Adds a component to be checked for upgrades. If the component exists it // it will be replaced and the return code is kReplaced. -// -// TODO(cpu): Evaluate if we want to support un-registration. ComponentUpdateService::Status CrxUpdateService::RegisterComponent( const CrxComponent& component) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -535,11 +466,8 @@ ComponentUpdateService::Status CrxUpdateService::RegisterComponent( !component.installer) return kError; - std::string id = - HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0], - component.pk_hash.size()/2))); - CrxUpdateItem* uit; - uit = FindUpdateItemById(id); + std::string id(component_updater::GetCrxComponentID(component)); + CrxUpdateItem* uit = FindUpdateItemById(id); if (uit) { uit->component = component; return kReplaced; @@ -617,32 +545,30 @@ void CrxUpdateService::GetComponents( it != work_items_.end(); ++it) { const CrxUpdateItem* item = *it; CrxComponentInfo info; - info.id = GetCrxComponentID(item->component); + info.id = component_updater::GetCrxComponentID(item->component); info.version = item->component.version.GetString(); info.name = item->component.name; components->push_back(info); } } -// This is the main loop of the component updater. +// This is the main loop of the component updater. It updates one component +// at a time if updates are available. Otherwise, it does an update check or +// takes a long sleep until the loop runs again. void CrxUpdateService::ProcessPendingItems() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + CrxUpdateItem* ready_upgrade = FindReadyComponent(); if (ready_upgrade) { UpdateComponent(ready_upgrade); return; } - 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. - ScheduleNextRun(kStepDelayLong); + + if (!CheckForUpdates()) + ScheduleNextRun(kStepDelayLong); } -CrxUpdateItem* CrxUpdateService::FindReadyComponent() { +CrxUpdateItem* CrxUpdateService::FindReadyComponent() const { class Helper { public: static bool IsReadyOnDemand(CrxUpdateItem* item) { @@ -653,7 +579,7 @@ CrxUpdateItem* CrxUpdateService::FindReadyComponent() { } }; - std::vector<CrxUpdateItem*>::iterator it = std::find_if( + std::vector<CrxUpdateItem*>::const_iterator it = std::find_if( work_items_.begin(), work_items_.end(), Helper::IsReadyOnDemand); if (it != work_items_.end()) return *it; @@ -663,6 +589,52 @@ CrxUpdateItem* CrxUpdateService::FindReadyComponent() { return NULL; } +// Prepares the components for an update check and initiates the request. +bool CrxUpdateService::CheckForUpdates() { + // All components are selected for the update check, regardless of when they + // were last checked. More selective algorithms could be implemented in the + // future. + std::vector<CrxUpdateItem*> items_to_check; + for (size_t i = 0; i != work_items_.size(); ++i) { + CrxUpdateItem* item = work_items_[i]; + DCHECK(item->status == CrxUpdateItem::kNew || + item->status == CrxUpdateItem::kNoUpdate || + item->status == CrxUpdateItem::kUpToDate || + item->status == CrxUpdateItem::kUpdated); + + ChangeItemState(item, CrxUpdateItem::kChecking); + + item->last_check = base::Time::Now(); + item->crx_urls.clear(); + item->crx_diffurls.clear(); + 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; + item->download_metrics.clear(); + + items_to_check.push_back(item); + } + + if (items_to_check.empty()) + return false; + + update_checker_ = component_updater::UpdateChecker::Create( + config_->UpdateUrl(), + config_->RequestContext(), + base::Bind(&CrxUpdateService::UpdateCheckComplete, + base::Unretained(this))).Pass(); + return update_checker_->CheckForUpdates(items_to_check, + config_->ExtraRequestParams()); +} + void CrxUpdateService::UpdateComponent(CrxUpdateItem* workitem) { scoped_ptr<CRXContext> crx_context(new CRXContext); crx_context->pk_hash = workitem->component.pk_hash; @@ -692,122 +664,24 @@ void CrxUpdateService::UpdateComponent(CrxUpdateItem* workitem) { crx_downloader_->StartDownload(*urls); } -// 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_urls.clear(); - item->crx_diffurls.clear(); - 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; - item->download_metrics.clear(); -} - -// 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) { - // All items are added to a single update check. - for (UpdateItems::const_iterator it = work_items_.begin(); - it != work_items_.end(); ++it) { - CrxUpdateItem* item = *it; - DCHECK(item->status == CrxUpdateItem::kNew || - item->status == CrxUpdateItem::kNoUpdate || - item->status == CrxUpdateItem::kUpToDate || - item->status == CrxUpdateItem::kUpdated); - AddItemToUpdateCheck(item, update_check_items); - } -} - -// 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, config_->ExtraRequestParams()), - MakeContextDelegate(this, new UpdateContext()), - config_->RequestContext())); -} - -// Called when we got a response from the update server. It consists of an xml -// document following the omaha update scheme. -void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source, - UpdateContext* context) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (component_updater::FetchSuccess(*source)) { - std::string xml; - source->GetResponseAsString(&xml); - url_fetcher_.reset(); - ParseResponse(xml); - } else { - url_fetcher_.reset(); - CrxUpdateService::OnParseUpdateResponseFailed("network error"); - } - delete context; -} - -void CrxUpdateService::ParseResponse(const std::string& xml) { +void CrxUpdateService::UpdateCheckComplete( + int error, + const std::string& error_message, + const component_updater::UpdateResponse::Results& results) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - component_updater::UpdateResponse update_response; - if (update_response.Parse(xml)) - CrxUpdateService::OnParseUpdateResponseSucceeded(update_response.results()); + update_checker_.reset(); + if (!error) + OnUpdateCheckSucceeded(results); else - CrxUpdateService::OnParseUpdateResponseFailed(update_response.errors()); + OnUpdateCheckFailed(error, error_message); } -// A valid Omaha update check has arrived, from only the list of components that -// we are currently upgrading we check for a match in which the server side -// 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::OnParseUpdateResponseSucceeded( +// Handles a valid Omaha update check response by matching the results with +// the registered components which were checked for updates. +// If updates are found, prepare the components for the actual version upgrade. +// One of these components will be drafted for the upgrade next time +// ProcessPendingItems is called. +void CrxUpdateService::OnUpdateCheckSucceeded( const component_updater::UpdateResponse::Results& results) { size_t num_updates_pending = 0; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -881,9 +755,11 @@ void CrxUpdateService::OnParseUpdateResponseSucceeded( ScheduleNextRun(num_updates_pending > 0 ? kStepDelayShort : kStepDelayMedium); } -void CrxUpdateService::OnParseUpdateResponseFailed( - const std::string& error_message) { +// TODO: record UMA stats. +void CrxUpdateService::OnUpdateCheckFailed(int error, + const std::string& error_message) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(error); size_t count = ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kNoUpdate); DCHECK_GT(count, 0ul); diff --git a/chrome/browser/component_updater/component_updater_service.h b/chrome/browser/component_updater/component_updater_service.h index 8f3f8b6..0176386 100644 --- a/chrome/browser/component_updater/component_updater_service.h +++ b/chrome/browser/component_updater/component_updater_service.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 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. @@ -107,9 +107,6 @@ struct CrxComponent { ~CrxComponent(); }; -// This convenience function returns component id of given CrxComponent. -std::string GetCrxComponentID(const CrxComponent& component); - // Convenience structure to use with component listing / enumeration. struct CrxComponentInfo { // |id| is currently derived from |CrxComponent.pk_hash|, see rest of the diff --git a/chrome/browser/component_updater/component_updater_utils.cc b/chrome/browser/component_updater/component_updater_utils.cc index e8128ad..dafc272 100644 --- a/chrome/browser/component_updater/component_updater_utils.cc +++ b/chrome/browser/component_updater/component_updater_utils.cc @@ -6,12 +6,17 @@ #include "base/file_util.h" #include "base/files/file_path.h" #include "base/guid.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.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_service.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 "extensions/common/extension.h" #include "net/base/load_flags.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_request_context_getter.h" @@ -140,5 +145,28 @@ bool DeleteFileAndEmptyParentDirectory(const base::FilePath& filepath) { return base::DeleteFile(dirname, false); } +// Produces an extension-like friendly id. +std::string HexStringToID(const std::string& hexstr) { + std::string id; + for (size_t i = 0; i < hexstr.size(); ++i) { + int val = 0; + if (base::HexStringToInt(base::StringPiece(hexstr.begin() + i, + hexstr.begin() + i + 1), + &val)) { + id.append(1, val + 'a'); + } else { + id.append(1, 'a'); + } + } + DCHECK(extensions::Extension::IdIsValid(id)); + return id; +} + +std::string GetCrxComponentID(const CrxComponent& component) { + return component_updater::HexStringToID( + StringToLowerASCII(base::HexEncode(&component.pk_hash[0], + component.pk_hash.size()/2))); +} + } // namespace component_updater diff --git a/chrome/browser/component_updater/component_updater_utils.h b/chrome/browser/component_updater/component_updater_utils.h index 67aa0d1..d5431b6 100644 --- a/chrome/browser/component_updater/component_updater_utils.h +++ b/chrome/browser/component_updater/component_updater_utils.h @@ -7,6 +7,7 @@ #include <string> +struct CrxComponent; class GURL; namespace base { @@ -40,7 +41,8 @@ struct CrxUpdateItem; // Builds a protocol request string by creating the outer envelope for // the request and including the request body specified as a parameter. // If specified, |additional_attributes| are appended as attributes of the -// request element. +// request element. The additional attributes have to be well-formed for +// insertion in the request element. std::string BuildProtocolRequest(const std::string& request_body, const std::string& additional_attributes); @@ -73,6 +75,10 @@ bool IsHttpServerError(int status_code); // Returns true if the file and the empty directory are deleted. bool DeleteFileAndEmptyParentDirectory(const base::FilePath& filepath); +// Returns the component id of the |component|. The component id is in a +// format similar with the format of an extension id. +std::string GetCrxComponentID(const CrxComponent& component); + } // 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 3889db2..3148368 100644 --- a/chrome/browser/component_updater/test/component_updater_service_unittest.cc +++ b/chrome/browser/component_updater/test/component_updater_service_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 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. @@ -9,6 +9,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/values.h" +#include "chrome/browser/component_updater/component_updater_utils.h" #include "chrome/browser/component_updater/test/test_installer.h" #include "chrome/common/chrome_paths.h" #include "content/public/browser/browser_thread.h" @@ -39,6 +40,10 @@ MockComponentObserver::MockComponentObserver() { MockComponentObserver::~MockComponentObserver() { } +bool PartialMatch::Match(const std::string& actual) const { + return actual.find(expected_) != std::string::npos; +} + TestConfigurator::TestConfigurator() : initial_time_(0), times_(1), @@ -148,19 +153,6 @@ URLRequestPostInterceptor* InterceptorFactory::CreateInterceptor() { base::FilePath::FromUTF8Unsafe(POST_INTERCEPT_PATH)); } -class PartialMatch : public URLRequestPostInterceptor::RequestMatcher { - public: - explicit PartialMatch(const std::string& expected) : expected_(expected) {} - virtual bool Match(const std::string& actual) const OVERRIDE { - return actual.find(expected_) != std::string::npos; - } - - private: - const std::string expected_; - - DISALLOW_COPY_AND_ASSIGN(PartialMatch); -}; - ComponentUpdaterTest::ComponentUpdaterTest() : test_config_(NULL), thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) { 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 b776f29..62919dc 100644 --- a/chrome/browser/component_updater/test/component_updater_service_unittest.h +++ b/chrome/browser/component_updater/test/component_updater_service_unittest.h @@ -10,6 +10,7 @@ #include <string> #include <utility> #include <vector> + #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/files/file_path.h" @@ -43,6 +44,16 @@ class InterceptorFactory : public URLRequestPostInterceptorFactory { DISALLOW_COPY_AND_ASSIGN(InterceptorFactory); }; +class PartialMatch : public URLRequestPostInterceptor::RequestMatcher { + public: + explicit PartialMatch(const std::string& expected) : expected_(expected) {} + virtual bool Match(const std::string& actual) const OVERRIDE; + private: + const std::string expected_; + + DISALLOW_COPY_AND_ASSIGN(PartialMatch); +}; + // component 1 has extension id "jebgalgnebhfojomionfpkfelancnnkf", and // the RSA public key the following hash: const uint8 jebg_hash[] = {0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec, diff --git a/chrome/browser/component_updater/test/update_checker_unittest.cc b/chrome/browser/component_updater/test/update_checker_unittest.cc new file mode 100644 index 0000000..9ec39e2 --- /dev/null +++ b/chrome/browser/component_updater/test/update_checker_unittest.cc @@ -0,0 +1,232 @@ +// Copyright 2014 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/basictypes.h" +#include "base/compiler_specific.h" +#include "base/file_util.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/version.h" +#include "chrome/browser/component_updater/crx_update_item.h" +#include "chrome/browser/component_updater/test/component_updater_service_unittest.h" +#include "chrome/browser/component_updater/test/url_request_post_interceptor.h" +#include "chrome/browser/component_updater/update_checker.h" +#include "chrome/common/chrome_paths.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::BrowserThread; + +namespace component_updater { + +base::FilePath test_file(const char* file) { + base::FilePath path; + PathService::Get(chrome::DIR_TEST_DATA, &path); + return path.AppendASCII("components").AppendASCII(file); +} + +class UpdateCheckerTest : public testing::Test { + public: + UpdateCheckerTest(); + virtual ~UpdateCheckerTest(); + + // Overrides from testing::Test. + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + void UpdateCheckComplete(int error, + const std::string& error_message, + const UpdateResponse::Results& results); + + net::URLRequestContextGetter* context() { + return context_.get(); + } + + protected: + void Quit(); + void RunThreads(); + void RunThreadsUntilIdle(); + + CrxUpdateItem BuildCrxUpdateItem(); + + scoped_ptr<UpdateChecker> update_checker_; + + scoped_ptr<InterceptorFactory> interceptor_factory_; + URLRequestPostInterceptor* post_interceptor_; // Owned by the factory. + + int error_; + std::string error_message_; + UpdateResponse::Results results_; + + private: + scoped_refptr<net::TestURLRequestContextGetter> context_; + content::TestBrowserThreadBundle thread_bundle_; + base::FilePath test_data_dir_; + base::Closure quit_closure_; + + DISALLOW_COPY_AND_ASSIGN(UpdateCheckerTest); +}; + +UpdateCheckerTest::UpdateCheckerTest() + : error_(0), + context_(new net::TestURLRequestContextGetter( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))), + thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) { + // The test directory is chrome/test/data/components. + PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_); + test_data_dir_ = test_data_dir_.AppendASCII("components"); + + net::URLFetcher::SetEnableInterceptionForTests(true); +} + +UpdateCheckerTest::~UpdateCheckerTest() { + net::URLFetcher::SetEnableInterceptionForTests(false); + context_ = NULL; +} + +void UpdateCheckerTest::SetUp() { + interceptor_factory_.reset(new InterceptorFactory); + post_interceptor_ = interceptor_factory_->CreateInterceptor(); + EXPECT_TRUE(post_interceptor_); + + update_checker_.reset(); + + error_ = 0; + error_message_.clear(); + results_ = UpdateResponse::Results(); +} + +void UpdateCheckerTest::TearDown() { + update_checker_.reset(); + + post_interceptor_ = NULL; + interceptor_factory_.reset(); +} + +void UpdateCheckerTest::RunThreads() { + base::RunLoop runloop; + quit_closure_ = 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 UpdateCheckerTest::RunThreadsUntilIdle() { + base::RunLoop().RunUntilIdle(); +} + +void UpdateCheckerTest::Quit() { + if (!quit_closure_.is_null()) + quit_closure_.Run(); +} + +void UpdateCheckerTest::UpdateCheckComplete( + int error, + const std::string& error_message, + const UpdateResponse::Results& results) { + error_ = error; + error_message_ = error_message; + results_ = results; + Quit(); +} + +CrxUpdateItem UpdateCheckerTest::BuildCrxUpdateItem() { + CrxComponent crx_component; + crx_component.name = "test_jebg"; + crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx_component.installer = NULL; + crx_component.observer = NULL; + crx_component.version = base::Version("0.9"); + crx_component.fingerprint = "fp1"; + + CrxUpdateItem crx_update_item; + crx_update_item.status = CrxUpdateItem::kNew; + crx_update_item.id = "jebgalgnebhfojomionfpkfelancnnkf"; + crx_update_item.component = crx_component; + + return crx_update_item; +} + +TEST_F(UpdateCheckerTest, UpdateCheckSuccess) { + EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch( + "updatecheck"), test_file("updatecheck_reply_1.xml"))); + + update_checker_ = UpdateChecker::Create( + GURL("http://localhost2/update2"), + context(), + base::Bind(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))).Pass(); + + CrxUpdateItem item(BuildCrxUpdateItem()); + std::vector<CrxUpdateItem*> items_to_check; + items_to_check.push_back(&item); + + update_checker_->CheckForUpdates(items_to_check, "extra=\"params\""); + + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + // Sanity check the request. + EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find( + "request protocol=\"3.0\" extra=\"params\"")); + EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find( + "app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">" + "<updatecheck /><packages><package fp=\"fp1\"/></packages></app>")); + + // Sanity check the arguments of the callback after parsing. + EXPECT_EQ(0, error_); + EXPECT_TRUE(error_message_.empty()); + EXPECT_EQ(1ul, results_.list.size()); + EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", + results_.list[0].extension_id.c_str()); + EXPECT_STREQ("1.0", results_.list[0].manifest.version.c_str()); +} + +TEST_F(UpdateCheckerTest, UpdateNetworkError) { + // Setting this expectation simulates a network error since the + // file is not found. Since setting the expectation fails, this function + // owns |request_matcher|. + scoped_ptr<PartialMatch> request_matcher( new PartialMatch("updatecheck")); + EXPECT_FALSE(post_interceptor_->ExpectRequest(request_matcher.get(), + test_file("no such file"))); + + update_checker_ = UpdateChecker::Create( + GURL("http://localhost2/update2"), + context(), + base::Bind(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))).Pass(); + + CrxUpdateItem item(BuildCrxUpdateItem()); + std::vector<CrxUpdateItem*> items_to_check; + items_to_check.push_back(&item); + + update_checker_->CheckForUpdates(items_to_check, ""); + + RunThreads(); + + EXPECT_EQ(0, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + EXPECT_NE(0, error_); + EXPECT_STREQ("network error", error_message_.c_str()); + EXPECT_EQ(0ul, results_.list.size()); +} + +} // namespace component_updater + diff --git a/chrome/browser/component_updater/update_checker.cc b/chrome/browser/component_updater/update_checker.cc new file mode 100644 index 0000000..92d91d3 --- /dev/null +++ b/chrome/browser/component_updater/update_checker.cc @@ -0,0 +1,154 @@ +// Copyright 2014 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_checker.h" + +#include "base/compiler_specific.h" +#include "base/strings/stringprintf.h" +#include "chrome/browser/component_updater/component_updater_utils.h" +#include "chrome/browser/component_updater/crx_update_item.h" +#include "content/public/browser/browser_thread.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "url/gurl.h" + +using content::BrowserThread; + +namespace component_updater { + +// Builds an update check request for |components|. |additional_attributes| is +// serialized as part of the <request> element of the request to customize it +// with data that is not platform or component specific. For each |item|, a +// corresponding <app> element is created and inserted as a child node of +// the <request>. +// +// An app element looks like this: +// <app appid="hnimpnehoodheedghdeeijklkeaacbdc" +// version="0.1.2.3" installsource="ondemand"> +// <updatecheck /> +// <packages> +// <package fp="abcd" /> +// </packages> +// </app> +std::string BuildUpdateCheckRequest(const std::vector<CrxUpdateItem*>& items, + const std::string& additional_attributes) { + std::string app_elements; + for (size_t i = 0; i != items.size(); ++i) { + const CrxUpdateItem* item = items[i]; + std::string app("<app "); + base::StringAppendF(&app, + "appid=\"%s\" version=\"%s\"", + item->id.c_str(), + item->component.version.GetString().c_str()); + if (item->on_demand) + base::StringAppendF(&app, " installsource=\"ondemand\""); + base::StringAppendF(&app, ">"); + base::StringAppendF(&app, "<updatecheck />"); + if (!item->component.fingerprint.empty()) { + base::StringAppendF(&app, + "<packages>" + "<package fp=\"%s\"/>" + "</packages>", + item->component.fingerprint.c_str()); + } + base::StringAppendF(&app, "</app>"); + app_elements.append(app); + } + + return BuildProtocolRequest(app_elements, additional_attributes); +} + +class UpdateCheckerImpl + : public UpdateChecker, + public net::URLFetcherDelegate { + public: + UpdateCheckerImpl(const GURL& url, + net::URLRequestContextGetter* url_request_context_getter, + const UpdateCheckCallback& update_check_callback); + virtual ~UpdateCheckerImpl(); + + // Overrides for UpdateChecker. + virtual bool CheckForUpdates( + const std::vector<CrxUpdateItem*>& items_to_check, + const std::string& additional_attributes) OVERRIDE; + + // Overrides for UrlFetcher. + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + + private: + const GURL url_; + net::URLRequestContextGetter* url_request_context_getter_; // Not owned. + const UpdateCheckCallback update_check_callback_; + + scoped_ptr<net::URLFetcher> url_fetcher_; + + DISALLOW_COPY_AND_ASSIGN(UpdateCheckerImpl); +}; + +scoped_ptr<UpdateChecker> UpdateChecker::Create( + const GURL& url, + net::URLRequestContextGetter* url_request_context_getter, + const UpdateCheckCallback& update_check_callback) { + scoped_ptr<UpdateCheckerImpl> update_checker(new UpdateCheckerImpl( + url, url_request_context_getter, update_check_callback)); + return update_checker.PassAs<UpdateChecker>(); +} + +UpdateCheckerImpl::UpdateCheckerImpl( + const GURL& url, + net::URLRequestContextGetter* url_request_context_getter, + const UpdateCheckCallback& update_check_callback) + : url_(url), + url_request_context_getter_(url_request_context_getter), + update_check_callback_(update_check_callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +UpdateCheckerImpl::~UpdateCheckerImpl() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +bool UpdateCheckerImpl::CheckForUpdates( + const std::vector<CrxUpdateItem*>& items_to_check, + const std::string& additional_attributes) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (url_fetcher_) + return false; // Another fetch is in progress. + + url_fetcher_.reset(SendProtocolRequest( + url_, + BuildUpdateCheckRequest(items_to_check, additional_attributes), + this, + url_request_context_getter_)); + + return true; +} + +void UpdateCheckerImpl::OnURLFetchComplete(const net::URLFetcher* source) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(url_fetcher_.get() == source); + + int error = 0; + std::string error_message; + component_updater::UpdateResponse update_response; + + if (component_updater::FetchSuccess(*source)) { + std::string xml; + source->GetResponseAsString(&xml); + if (!update_response.Parse(xml)) { + error = -1; + error_message = update_response.errors(); + } + } else { + error = component_updater::GetFetchError(*source); + error_message.assign("network error"); + } + + url_fetcher_.reset(); + update_check_callback_.Run(error, error_message, update_response.results()); +} + +} // namespace component_updater + diff --git a/chrome/browser/component_updater/update_checker.h b/chrome/browser/component_updater/update_checker.h new file mode 100644 index 0000000..7120b69 --- /dev/null +++ b/chrome/browser/component_updater/update_checker.h @@ -0,0 +1,55 @@ +// Copyright 2014 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_CHECKER_H_ +#define CHROME_BROWSER_COMPONENT_UPDATER_UPDATE_CHECKER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/component_updater/update_response.h" + +class GURL; + +namespace net { +class URLRequestContextGetter; +} + +namespace component_updater { + +struct CrxUpdateItem; + +class UpdateChecker { + public: + typedef base::Callback<void (int error, const std::string& error_message, + const UpdateResponse::Results& results)> UpdateCheckCallback; + + virtual ~UpdateChecker() {} + + // Initiates an update check for the |items_to_check|. |additional_attributes| + // provides a way to customize the <request> element. This value is inserted + // as-is, therefore it must be well-formed as an XML attribute string. + virtual bool CheckForUpdates( + const std::vector<CrxUpdateItem*>& items_to_check, + const std::string& additional_attributes) = 0; + + static scoped_ptr<UpdateChecker> Create( + const GURL& url, + net::URLRequestContextGetter* url_request_context_getter, + const UpdateCheckCallback& update_check_callback); + + protected: + UpdateChecker() {} + + private: + DISALLOW_COPY_AND_ASSIGN(UpdateChecker); +}; + +} // namespace component_updater + +#endif // CHROME_BROWSER_COMPONENT_UPDATER_UPDATE_CHECKER_H_ + diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index a0e57d9..0b172ec 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -463,6 +463,8 @@ 'browser/component_updater/recovery_component_installer.h', 'browser/component_updater/swiftshader_component_installer.cc', 'browser/component_updater/swiftshader_component_installer.h', + 'browser/component_updater/update_checker.cc', + 'browser/component_updater/update_checker.h', 'browser/component_updater/update_response.cc', 'browser/component_updater/update_response.h', 'browser/component_updater/url_fetcher_downloader.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 2b0fb83..4df2a78 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -757,6 +757,7 @@ 'browser/component_updater/test/component_updater_service_unittest.cc', 'browser/component_updater/test/crx_downloader_unittest.cc', 'browser/component_updater/test/test_installer.cc', + 'browser/component_updater/test/update_checker_unittest.cc', 'browser/component_updater/test/update_response_unittest.cc', 'browser/component_updater/test/url_request_post_interceptor.cc', 'browser/content_settings/content_settings_default_provider_unittest.cc', |