blob: de4a7741b415a8a829f6163bb71e25fe3e34cc57 [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 Jianu72d8fe0d2017-07-11 18:42:1614#include "base/task_scheduler/post_task.h"
15#include "base/task_scheduler/task_traits.h"
gab7966d312016-05-11 20:35:0116#include "base/threading/thread_task_runner_handle.h"
avi5dd91f82015-12-25 22:30:4617#include "build/build_config.h"
[email protected]3cb2a4f2013-12-07 21:54:3418#if defined(OS_WIN)
sorin52ac0882015-01-24 01:15:0019#include "components/update_client/background_downloader_win.h"
[email protected]3cb2a4f2013-12-07 21:54:3420#endif
sorin7b8650522016-11-02 18:23:4121#include "components/update_client/update_client_errors.h"
sorin74e70672016-02-03 03:13:1022#include "components/update_client/url_fetcher_downloader.h"
23#include "components/update_client/utils.h"
[email protected]3cb2a4f2013-12-07 21:54:3424
sorin52ac0882015-01-24 01:15:0025namespace update_client {
[email protected]afa378f22013-12-02 03:37:5426
[email protected]8a5ebd432014-05-02 00:21:2227CrxDownloader::Result::Result()
28 : error(0), downloaded_bytes(-1), total_bytes(-1) {
29}
[email protected]3a0092d2013-12-18 03:04:3530
31CrxDownloader::DownloadMetrics::DownloadMetrics()
32 : downloader(kNone),
33 error(0),
[email protected]8a5ebd432014-05-02 00:21:2234 downloaded_bytes(-1),
35 total_bytes(-1),
36 download_time_ms(0) {
37}
[email protected]3a0092d2013-12-18 03:04:3538
[email protected]3cb2a4f2013-12-07 21:54:3439// On Windows, the first downloader in the chain is a background downloader,
40// which uses the BITS service.
dchengd0fc6aa92016-04-22 18:03:1241std::unique_ptr<CrxDownloader> CrxDownloader::Create(
[email protected]3cb2a4f2013-12-07 21:54:3442 bool is_background_download,
Sorin Jianu72d8fe0d2017-07-11 18:42:1643 net::URLRequestContextGetter* context_getter) {
dchengd0fc6aa92016-04-22 18:03:1244 std::unique_ptr<CrxDownloader> url_fetcher_downloader(
45 std::unique_ptr<CrxDownloader>(new UrlFetcherDownloader(
Sorin Jianu72d8fe0d2017-07-11 18:42:1646 std::unique_ptr<CrxDownloader>(), context_getter)));
[email protected]d0c8b8b42014-05-06 05:11:4547#if defined(OS_WIN)
[email protected]3cb2a4f2013-12-07 21:54:3448 if (is_background_download) {
dchengd0fc6aa92016-04-22 18:03:1249 return std::unique_ptr<CrxDownloader>(new BackgroundDownloader(
Sorin Jianu72d8fe0d2017-07-11 18:42:1650 std::move(url_fetcher_downloader), context_getter));
[email protected]3cb2a4f2013-12-07 21:54:3451 }
52#endif
[email protected]afa378f22013-12-02 03:37:5453
sorin9797aba2015-04-17 17:15:0354 return url_fetcher_downloader;
[email protected]afa378f22013-12-02 03:37:5455}
56
Sorin Jianu72d8fe0d2017-07-11 18:42:1657CrxDownloader::CrxDownloader(std::unique_ptr<CrxDownloader> successor)
58 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
sorin74e70672016-02-03 03:13:1059 successor_(std::move(successor)) {}
[email protected]afa378f22013-12-02 03:37:5460
sorin74e70672016-02-03 03:13:1061CrxDownloader::~CrxDownloader() {}
[email protected]afa378f22013-12-02 03:37:5462
[email protected]8a5ebd432014-05-02 00:21:2263void CrxDownloader::set_progress_callback(
64 const ProgressCallback& progress_callback) {
65 progress_callback_ = progress_callback;
66}
67
[email protected]3a0092d2013-12-18 03:04:3568GURL CrxDownloader::url() const {
69 return current_url_ != urls_.end() ? *current_url_ : GURL();
70}
71
72const std::vector<CrxDownloader::DownloadMetrics>
73CrxDownloader::download_metrics() const {
74 if (!successor_)
75 return download_metrics_;
76
77 std::vector<DownloadMetrics> retval(successor_->download_metrics());
sorin52ac0882015-01-24 01:15:0078 retval.insert(retval.begin(), download_metrics_.begin(),
79 download_metrics_.end());
[email protected]3a0092d2013-12-18 03:04:3580 return retval;
81}
82
[email protected]1b6587dc52014-04-26 00:38:5583void CrxDownloader::StartDownloadFromUrl(
84 const GURL& url,
sorin74e70672016-02-03 03:13:1085 const std::string& expected_hash,
[email protected]1b6587dc52014-04-26 00:38:5586 const DownloadCallback& download_callback) {
[email protected]afa378f22013-12-02 03:37:5487 std::vector<GURL> urls;
88 urls.push_back(url);
sorin74e70672016-02-03 03:13:1089 StartDownload(urls, expected_hash, download_callback);
[email protected]afa378f22013-12-02 03:37:5490}
91
[email protected]1b6587dc52014-04-26 00:38:5592void CrxDownloader::StartDownload(const std::vector<GURL>& urls,
sorin74e70672016-02-03 03:13:1093 const std::string& expected_hash,
[email protected]1b6587dc52014-04-26 00:38:5594 const DownloadCallback& download_callback) {
[email protected]ed6fb982014-07-23 16:56:5295 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]afa378f22013-12-02 03:37:5496
sorin7b8650522016-11-02 18:23:4197 auto error = CrxDownloaderError::NONE;
[email protected]da37c1d2013-12-19 01:04:3898 if (urls.empty()) {
sorin7b8650522016-11-02 18:23:4199 error = CrxDownloaderError::NO_URL;
sorin74e70672016-02-03 03:13:10100 } else if (expected_hash.empty()) {
sorin7b8650522016-11-02 18:23:41101 error = CrxDownloaderError::NO_HASH;
sorin74e70672016-02-03 03:13:10102 }
103
sorin7b8650522016-11-02 18:23:41104 if (error != CrxDownloaderError::NONE) {
[email protected]da37c1d2013-12-19 01:04:38105 Result result;
sorin7b8650522016-11-02 18:23:41106 result.error = static_cast<int>(error);
Sorin Jianu72d8fe0d2017-07-11 18:42:16107 main_task_runner()->PostTask(FROM_HERE,
108 base::Bind(download_callback, result));
[email protected]da37c1d2013-12-19 01:04:38109 return;
110 }
[email protected]afa378f22013-12-02 03:37:54111
112 urls_ = urls;
sorin74e70672016-02-03 03:13:10113 expected_hash_ = expected_hash;
[email protected]3cb2a4f2013-12-07 21:54:34114 current_url_ = urls_.begin();
[email protected]1b6587dc52014-04-26 00:38:55115 download_callback_ = download_callback;
[email protected]afa378f22013-12-02 03:37:54116
[email protected]3cb2a4f2013-12-07 21:54:34117 DoStartDownload(*current_url_);
[email protected]afa378f22013-12-02 03:37:54118}
119
[email protected]3a0092d2013-12-18 03:04:35120void CrxDownloader::OnDownloadComplete(
121 bool is_handled,
122 const Result& result,
123 const DownloadMetrics& download_metrics) {
[email protected]ed6fb982014-07-23 16:56:52124 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]afa378f22013-12-02 03:37:54125
sorin7b8650522016-11-02 18:23:41126 if (!result.error)
Sorin Jianu72d8fe0d2017-07-11 18:42:16127
128 base::PostTaskWithTraits(
sorin74e70672016-02-03 03:13:10129 FROM_HERE,
Sorin Jianu72d8fe0d2017-07-11 18:42:16130 {base::MayBlock(), base::TaskPriority::BACKGROUND,
131 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
sorin74e70672016-02-03 03:13:10132 base::Bind(&CrxDownloader::VerifyResponse, base::Unretained(this),
133 is_handled, result, download_metrics));
134 else
135 main_task_runner()->PostTask(
136 FROM_HERE,
137 base::Bind(&CrxDownloader::HandleDownloadError, base::Unretained(this),
138 is_handled, result, download_metrics));
[email protected]afa378f22013-12-02 03:37:54139}
140
[email protected]8a5ebd432014-05-02 00:21:22141void CrxDownloader::OnDownloadProgress(const Result& result) {
[email protected]ed6fb982014-07-23 16:56:52142 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]afa378f22013-12-02 03:37:54143
[email protected]8a5ebd432014-05-02 00:21:22144 if (progress_callback_.is_null())
145 return;
146
147 progress_callback_.Run(result);
148}
149
sorin74e70672016-02-03 03:13:10150// The function mutates the values of the parameters |result| and
151// |download_metrics|.
152void CrxDownloader::VerifyResponse(bool is_handled,
153 Result result,
154 DownloadMetrics download_metrics) {
sorin7b8650522016-11-02 18:23:41155 DCHECK_EQ(0, result.error);
156 DCHECK_EQ(0, download_metrics.error);
sorin74e70672016-02-03 03:13:10157 DCHECK(is_handled);
158
159 if (VerifyFileHash256(result.response, expected_hash_)) {
160 download_metrics_.push_back(download_metrics);
161 main_task_runner()->PostTask(FROM_HERE,
162 base::Bind(download_callback_, result));
163 return;
164 }
165
166 // The download was successful but the response is not trusted. Clean up
167 // the download, mutate the result, and try the remaining fallbacks when
168 // handling the error.
sorin7b8650522016-11-02 18:23:41169 result.error = static_cast<int>(CrxDownloaderError::BAD_HASH);
sorin74e70672016-02-03 03:13:10170 download_metrics.error = result.error;
171 DeleteFileAndEmptyParentDirectory(result.response);
172 result.response.clear();
173
174 main_task_runner()->PostTask(
175 FROM_HERE,
176 base::Bind(&CrxDownloader::HandleDownloadError, base::Unretained(this),
177 is_handled, result, download_metrics));
178}
179
180void CrxDownloader::HandleDownloadError(
181 bool is_handled,
182 const Result& result,
183 const DownloadMetrics& download_metrics) {
184 DCHECK(thread_checker_.CalledOnValidThread());
sorin7b8650522016-11-02 18:23:41185 DCHECK_NE(0, result.error);
186 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,
216 base::Bind(download_callback_, result));
217}
218
sorin52ac0882015-01-24 01:15:00219} // namespace update_client