|  | // 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 "components/update_client/crx_downloader.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/location.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/sequenced_task_runner.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "build/build_config.h" | 
|  | #if defined(OS_WIN) | 
|  | #include "components/update_client/background_downloader_win.h" | 
|  | #endif | 
|  | #include "components/update_client/update_client_errors.h" | 
|  | #include "components/update_client/url_fetcher_downloader.h" | 
|  | #include "components/update_client/utils.h" | 
|  |  | 
|  | namespace update_client { | 
|  |  | 
|  | CrxDownloader::Result::Result() | 
|  | : error(0), downloaded_bytes(-1), total_bytes(-1) { | 
|  | } | 
|  |  | 
|  | CrxDownloader::DownloadMetrics::DownloadMetrics() | 
|  | : downloader(kNone), | 
|  | error(0), | 
|  | downloaded_bytes(-1), | 
|  | total_bytes(-1), | 
|  | download_time_ms(0) { | 
|  | } | 
|  |  | 
|  | // On Windows, the first downloader in the chain is a background downloader, | 
|  | // which uses the BITS service. | 
|  | std::unique_ptr<CrxDownloader> CrxDownloader::Create( | 
|  | bool is_background_download, | 
|  | net::URLRequestContextGetter* context_getter, | 
|  | const scoped_refptr<base::SequencedTaskRunner>& task_runner) { | 
|  | std::unique_ptr<CrxDownloader> url_fetcher_downloader( | 
|  | std::unique_ptr<CrxDownloader>(new UrlFetcherDownloader( | 
|  | std::unique_ptr<CrxDownloader>(), context_getter, task_runner))); | 
|  | #if defined(OS_WIN) | 
|  | if (is_background_download) { | 
|  | return std::unique_ptr<CrxDownloader>(new BackgroundDownloader( | 
|  | std::move(url_fetcher_downloader), context_getter, task_runner)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | return url_fetcher_downloader; | 
|  | } | 
|  |  | 
|  | CrxDownloader::CrxDownloader( | 
|  | const scoped_refptr<base::SequencedTaskRunner>& task_runner, | 
|  | std::unique_ptr<CrxDownloader> successor) | 
|  | : task_runner_(task_runner), | 
|  | main_task_runner_(base::ThreadTaskRunnerHandle::Get()), | 
|  | successor_(std::move(successor)) {} | 
|  |  | 
|  | CrxDownloader::~CrxDownloader() {} | 
|  |  | 
|  | void CrxDownloader::set_progress_callback( | 
|  | const ProgressCallback& progress_callback) { | 
|  | progress_callback_ = progress_callback; | 
|  | } | 
|  |  | 
|  | GURL CrxDownloader::url() const { | 
|  | return current_url_ != urls_.end() ? *current_url_ : GURL(); | 
|  | } | 
|  |  | 
|  | const std::vector<CrxDownloader::DownloadMetrics> | 
|  | CrxDownloader::download_metrics() const { | 
|  | if (!successor_) | 
|  | return download_metrics_; | 
|  |  | 
|  | std::vector<DownloadMetrics> retval(successor_->download_metrics()); | 
|  | retval.insert(retval.begin(), download_metrics_.begin(), | 
|  | download_metrics_.end()); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | void CrxDownloader::StartDownloadFromUrl( | 
|  | const GURL& url, | 
|  | const std::string& expected_hash, | 
|  | const DownloadCallback& download_callback) { | 
|  | std::vector<GURL> urls; | 
|  | urls.push_back(url); | 
|  | StartDownload(urls, expected_hash, download_callback); | 
|  | } | 
|  |  | 
|  | void CrxDownloader::StartDownload(const std::vector<GURL>& urls, | 
|  | const std::string& expected_hash, | 
|  | const DownloadCallback& download_callback) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  |  | 
|  | auto error = CrxDownloaderError::NONE; | 
|  | if (urls.empty()) { | 
|  | error = CrxDownloaderError::NO_URL; | 
|  | } else if (expected_hash.empty()) { | 
|  | error = CrxDownloaderError::NO_HASH; | 
|  | } | 
|  |  | 
|  | if (error != CrxDownloaderError::NONE) { | 
|  | Result result; | 
|  | result.error = static_cast<int>(error); | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::Bind(download_callback, result)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | urls_ = urls; | 
|  | expected_hash_ = expected_hash; | 
|  | current_url_ = urls_.begin(); | 
|  | download_callback_ = download_callback; | 
|  |  | 
|  | DoStartDownload(*current_url_); | 
|  | } | 
|  |  | 
|  | void CrxDownloader::OnDownloadComplete( | 
|  | bool is_handled, | 
|  | const Result& result, | 
|  | const DownloadMetrics& download_metrics) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  |  | 
|  | if (!result.error) | 
|  | task_runner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&CrxDownloader::VerifyResponse, base::Unretained(this), | 
|  | is_handled, result, download_metrics)); | 
|  | else | 
|  | main_task_runner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&CrxDownloader::HandleDownloadError, base::Unretained(this), | 
|  | is_handled, result, download_metrics)); | 
|  | } | 
|  |  | 
|  | void CrxDownloader::OnDownloadProgress(const Result& result) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  |  | 
|  | if (progress_callback_.is_null()) | 
|  | return; | 
|  |  | 
|  | progress_callback_.Run(result); | 
|  | } | 
|  |  | 
|  | // The function mutates the values of the parameters |result| and | 
|  | // |download_metrics|. | 
|  | void CrxDownloader::VerifyResponse(bool is_handled, | 
|  | Result result, | 
|  | DownloadMetrics download_metrics) { | 
|  | DCHECK(task_runner()->RunsTasksOnCurrentThread()); | 
|  | DCHECK_EQ(0, result.error); | 
|  | DCHECK_EQ(0, download_metrics.error); | 
|  | DCHECK(is_handled); | 
|  |  | 
|  | if (VerifyFileHash256(result.response, expected_hash_)) { | 
|  | download_metrics_.push_back(download_metrics); | 
|  | main_task_runner()->PostTask(FROM_HERE, | 
|  | base::Bind(download_callback_, result)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The download was successful but the response is not trusted. Clean up | 
|  | // the download, mutate the result, and try the remaining fallbacks when | 
|  | // handling the error. | 
|  | result.error = static_cast<int>(CrxDownloaderError::BAD_HASH); | 
|  | download_metrics.error = result.error; | 
|  | DeleteFileAndEmptyParentDirectory(result.response); | 
|  | result.response.clear(); | 
|  |  | 
|  | main_task_runner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&CrxDownloader::HandleDownloadError, base::Unretained(this), | 
|  | is_handled, result, download_metrics)); | 
|  | } | 
|  |  | 
|  | void CrxDownloader::HandleDownloadError( | 
|  | bool is_handled, | 
|  | const Result& result, | 
|  | const DownloadMetrics& download_metrics) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | DCHECK_NE(0, result.error); | 
|  | DCHECK_NE(0, download_metrics.error); | 
|  |  | 
|  | download_metrics_.push_back(download_metrics); | 
|  |  | 
|  | // If an error has occured, try the next url if there is any, | 
|  | // or try the successor in the chain if there is any successor. | 
|  | // If this downloader has received a 5xx error for the current url, | 
|  | // as indicated by the |is_handled| flag, remove that url from the list of | 
|  | // urls so the url is never tried again down the chain. | 
|  | if (is_handled) { | 
|  | current_url_ = urls_.erase(current_url_); | 
|  | } else { | 
|  | ++current_url_; | 
|  | } | 
|  |  | 
|  | // Try downloading from another url from the list. | 
|  | if (current_url_ != urls_.end()) { | 
|  | DoStartDownload(*current_url_); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Try downloading using the next downloader. | 
|  | if (successor_ && !urls_.empty()) { | 
|  | successor_->StartDownload(urls_, expected_hash_, download_callback_); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The download ends here since there is no url nor downloader to handle this | 
|  | // download request further. | 
|  | main_task_runner()->PostTask(FROM_HERE, | 
|  | base::Bind(download_callback_, result)); | 
|  | } | 
|  |  | 
|  | }  // namespace update_client |