blob: ee80fc98a31c92b1c083f887f169b2562fa450ff [file] [log] [blame]
[email protected]de0fdca22014-08-19 05:26:091// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]afa378f22013-12-02 03:37:542// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
sorin52ac0882015-01-24 01:15:005#include "components/update_client/crx_downloader.h"
[email protected]ed6fb982014-07-23 16:56:526
dcheng51ace48a2015-12-26 22:45:177#include <utility>
8
sorin6bb8de42015-06-03 00:23:279#include "base/bind.h"
sorin74e70672016-02-03 03:13:1010#include "base/bind_helpers.h"
11#include "base/files/file_util.h"
sorin6bb8de42015-06-03 00:23:2712#include "base/location.h"
[email protected]ed6fb982014-07-23 16:56:5213#include "base/logging.h"
Sorin Jianuebd652462017-07-23 02:00:5814#include "base/memory/ptr_util.h"
Sorin Jianu72d8fe0d2017-07-11 18:42:1615#include "base/task_scheduler/post_task.h"
16#include "base/task_scheduler/task_traits.h"
gab7966d312016-05-11 20:35:0117#include "base/threading/thread_task_runner_handle.h"
avi5dd91f82015-12-25 22:30:4618#include "build/build_config.h"
[email protected]3cb2a4f2013-12-07 21:54:3419#if defined(OS_WIN)
sorin52ac0882015-01-24 01:15:0020#include "components/update_client/background_downloader_win.h"
[email protected]3cb2a4f2013-12-07 21:54:3421#endif
Sorin Jianuebd652462017-07-23 02:00:5822#include "components/update_client/task_traits.h"
sorin7b8650522016-11-02 18:23:4123#include "components/update_client/update_client_errors.h"
sorin74e70672016-02-03 03:13:1024#include "components/update_client/url_fetcher_downloader.h"
25#include "components/update_client/utils.h"
[email protected]3cb2a4f2013-12-07 21:54:3426
sorin52ac0882015-01-24 01:15:0027namespace update_client {
[email protected]afa378f22013-12-02 03:37:5428
[email protected]8a5ebd432014-05-02 00:21:2229CrxDownloader::Result::Result()
30 : error(0), downloaded_bytes(-1), total_bytes(-1) {
31}
[email protected]3a0092d2013-12-18 03:04:3532
33CrxDownloader::DownloadMetrics::DownloadMetrics()
34 : downloader(kNone),
35 error(0),
[email protected]8a5ebd432014-05-02 00:21:2236 downloaded_bytes(-1),
37 total_bytes(-1),
38 download_time_ms(0) {
39}
[email protected]3a0092d2013-12-18 03:04:3540
[email protected]3cb2a4f2013-12-07 21:54:3441// On Windows, the first downloader in the chain is a background downloader,
42// which uses the BITS service.
dchengd0fc6aa92016-04-22 18:03:1243std::unique_ptr<CrxDownloader> CrxDownloader::Create(
[email protected]3cb2a4f2013-12-07 21:54:3444 bool is_background_download,
Sorin Jianu72d8fe0d2017-07-11 18:42:1645 net::URLRequestContextGetter* context_getter) {
Sorin Jianuebd652462017-07-23 02:00:5846 std::unique_ptr<CrxDownloader> url_fetcher_downloader =
47 base::MakeUnique<UrlFetcherDownloader>(nullptr, context_getter);
48
[email protected]d0c8b8b42014-05-06 05:11:4549#if defined(OS_WIN)
[email protected]3cb2a4f2013-12-07 21:54:3450 if (is_background_download) {
Sorin Jianuebd652462017-07-23 02:00:5851 return base::MakeUnique<BackgroundDownloader>(
52 std::move(url_fetcher_downloader));
[email protected]3cb2a4f2013-12-07 21:54:3453 }
54#endif
[email protected]afa378f22013-12-02 03:37:5455
sorin9797aba2015-04-17 17:15:0356 return url_fetcher_downloader;
[email protected]afa378f22013-12-02 03:37:5457}
58
Sorin Jianu72d8fe0d2017-07-11 18:42:1659CrxDownloader::CrxDownloader(std::unique_ptr<CrxDownloader> successor)
60 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
sorin74e70672016-02-03 03:13:1061 successor_(std::move(successor)) {}
[email protected]afa378f22013-12-02 03:37:5462
sorin74e70672016-02-03 03:13:1063CrxDownloader::~CrxDownloader() {}
[email protected]afa378f22013-12-02 03:37:5464
[email protected]8a5ebd432014-05-02 00:21:2265void CrxDownloader::set_progress_callback(
66 const ProgressCallback& progress_callback) {
67 progress_callback_ = progress_callback;
68}
69
[email protected]3a0092d2013-12-18 03:04:3570GURL CrxDownloader::url() const {
71 return current_url_ != urls_.end() ? *current_url_ : GURL();
72}
73
74const std::vector<CrxDownloader::DownloadMetrics>
75CrxDownloader::download_metrics() const {
76 if (!successor_)
77 return download_metrics_;
78
79 std::vector<DownloadMetrics> retval(successor_->download_metrics());
sorin52ac0882015-01-24 01:15:0080 retval.insert(retval.begin(), download_metrics_.begin(),
81 download_metrics_.end());
[email protected]3a0092d2013-12-18 03:04:3582 return retval;
83}
84
[email protected]1b6587dc52014-04-26 00:38:5585void CrxDownloader::StartDownloadFromUrl(
86 const GURL& url,
sorin74e70672016-02-03 03:13:1087 const std::string& expected_hash,
[email protected]1b6587dc52014-04-26 00:38:5588 const DownloadCallback& download_callback) {
[email protected]afa378f22013-12-02 03:37:5489 std::vector<GURL> urls;
90 urls.push_back(url);
sorin74e70672016-02-03 03:13:1091 StartDownload(urls, expected_hash, download_callback);
[email protected]afa378f22013-12-02 03:37:5492}
93
[email protected]1b6587dc52014-04-26 00:38:5594void CrxDownloader::StartDownload(const std::vector<GURL>& urls,
sorin74e70672016-02-03 03:13:1095 const std::string& expected_hash,
[email protected]1b6587dc52014-04-26 00:38:5596 const DownloadCallback& download_callback) {
[email protected]ed6fb982014-07-23 16:56:5297 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]afa378f22013-12-02 03:37:5498
sorin7b8650522016-11-02 18:23:4199 auto error = CrxDownloaderError::NONE;
[email protected]da37c1d2013-12-19 01:04:38100 if (urls.empty()) {
sorin7b8650522016-11-02 18:23:41101 error = CrxDownloaderError::NO_URL;
sorin74e70672016-02-03 03:13:10102 } else if (expected_hash.empty()) {
sorin7b8650522016-11-02 18:23:41103 error = CrxDownloaderError::NO_HASH;
sorin74e70672016-02-03 03:13:10104 }
105
sorin7b8650522016-11-02 18:23:41106 if (error != CrxDownloaderError::NONE) {
[email protected]da37c1d2013-12-19 01:04:38107 Result result;
sorin7b8650522016-11-02 18:23:41108 result.error = static_cast<int>(error);
Sorin Jianu72d8fe0d2017-07-11 18:42:16109 main_task_runner()->PostTask(FROM_HERE,
Sorin Jianuebd652462017-07-23 02:00:58110 base::BindOnce(download_callback, result));
[email protected]da37c1d2013-12-19 01:04:38111 return;
112 }
[email protected]afa378f22013-12-02 03:37:54113
114 urls_ = urls;
sorin74e70672016-02-03 03:13:10115 expected_hash_ = expected_hash;
[email protected]3cb2a4f2013-12-07 21:54:34116 current_url_ = urls_.begin();
[email protected]1b6587dc52014-04-26 00:38:55117 download_callback_ = download_callback;
[email protected]afa378f22013-12-02 03:37:54118
[email protected]3cb2a4f2013-12-07 21:54:34119 DoStartDownload(*current_url_);
[email protected]afa378f22013-12-02 03:37:54120}
121
[email protected]3a0092d2013-12-18 03:04:35122void CrxDownloader::OnDownloadComplete(
123 bool is_handled,
124 const Result& result,
125 const DownloadMetrics& download_metrics) {
[email protected]ed6fb982014-07-23 16:56:52126 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]afa378f22013-12-02 03:37:54127
sorin7b8650522016-11-02 18:23:41128 if (!result.error)
Sorin Jianu72d8fe0d2017-07-11 18:42:16129 base::PostTaskWithTraits(
Sorin Jianuebd652462017-07-23 02:00:58130 FROM_HERE, kTaskTraits,
131 base::BindOnce(&CrxDownloader::VerifyResponse, base::Unretained(this),
132 is_handled, result, download_metrics));
sorin74e70672016-02-03 03:13:10133 else
134 main_task_runner()->PostTask(
Sorin Jianuebd652462017-07-23 02:00:58135 FROM_HERE, base::BindOnce(&CrxDownloader::HandleDownloadError,
136 base::Unretained(this), is_handled, result,
137 download_metrics));
[email protected]afa378f22013-12-02 03:37:54138}
139
[email protected]8a5ebd432014-05-02 00:21:22140void CrxDownloader::OnDownloadProgress(const Result& result) {
[email protected]ed6fb982014-07-23 16:56:52141 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]afa378f22013-12-02 03:37:54142
[email protected]8a5ebd432014-05-02 00:21:22143 if (progress_callback_.is_null())
144 return;
145
146 progress_callback_.Run(result);
147}
148
sorin74e70672016-02-03 03:13:10149// The function mutates the values of the parameters |result| and
150// |download_metrics|.
151void CrxDownloader::VerifyResponse(bool is_handled,
152 Result result,
153 DownloadMetrics download_metrics) {
sorin7b8650522016-11-02 18:23:41154 DCHECK_EQ(0, result.error);
155 DCHECK_EQ(0, download_metrics.error);
sorin74e70672016-02-03 03:13:10156 DCHECK(is_handled);
157
158 if (VerifyFileHash256(result.response, expected_hash_)) {
159 download_metrics_.push_back(download_metrics);
160 main_task_runner()->PostTask(FROM_HERE,
Sorin Jianuebd652462017-07-23 02:00:58161 base::BindOnce(download_callback_, result));
sorin74e70672016-02-03 03:13:10162 return;
163 }
164
165 // The download was successful but the response is not trusted. Clean up
166 // the download, mutate the result, and try the remaining fallbacks when
167 // handling the error.
sorin7b8650522016-11-02 18:23:41168 result.error = static_cast<int>(CrxDownloaderError::BAD_HASH);
sorin74e70672016-02-03 03:13:10169 download_metrics.error = result.error;
170 DeleteFileAndEmptyParentDirectory(result.response);
171 result.response.clear();
172
173 main_task_runner()->PostTask(
Sorin Jianuebd652462017-07-23 02:00:58174 FROM_HERE, base::BindOnce(&CrxDownloader::HandleDownloadError,
175 base::Unretained(this), is_handled, result,
176 download_metrics));
sorin74e70672016-02-03 03:13:10177}
178
179void CrxDownloader::HandleDownloadError(
180 bool is_handled,
181 const Result& result,
182 const DownloadMetrics& download_metrics) {
183 DCHECK(thread_checker_.CalledOnValidThread());
sorin7b8650522016-11-02 18:23:41184 DCHECK_NE(0, result.error);
Sorin Jianu52de5fa2017-10-09 23:19:33185 DCHECK(result.response.empty());
sorin7b8650522016-11-02 18:23:41186 DCHECK_NE(0, download_metrics.error);
sorin74e70672016-02-03 03:13:10187
188 download_metrics_.push_back(download_metrics);
189
190 // If an error has occured, try the next url if there is any,
191 // or try the successor in the chain if there is any successor.
192 // If this downloader has received a 5xx error for the current url,
193 // as indicated by the |is_handled| flag, remove that url from the list of
194 // urls so the url is never tried again down the chain.
195 if (is_handled) {
196 current_url_ = urls_.erase(current_url_);
197 } else {
198 ++current_url_;
199 }
200
201 // Try downloading from another url from the list.
202 if (current_url_ != urls_.end()) {
203 DoStartDownload(*current_url_);
204 return;
205 }
206
207 // Try downloading using the next downloader.
208 if (successor_ && !urls_.empty()) {
209 successor_->StartDownload(urls_, expected_hash_, download_callback_);
210 return;
211 }
212
213 // The download ends here since there is no url nor downloader to handle this
214 // download request further.
215 main_task_runner()->PostTask(FROM_HERE,
Sorin Jianuebd652462017-07-23 02:00:58216 base::BindOnce(download_callback_, result));
sorin74e70672016-02-03 03:13:10217}
218
sorin52ac0882015-01-24 01:15:00219} // namespace update_client