crx_downloader.cc 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/update_client/crx_downloader.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/check_op.h"
  8. #include "base/files/file_util.h"
  9. #include "base/location.h"
  10. #include "base/task/thread_pool.h"
  11. #include "base/threading/thread_task_runner_handle.h"
  12. #include "build/build_config.h"
  13. #if BUILDFLAG(IS_WIN)
  14. #include "components/update_client/background_downloader_win.h"
  15. #endif
  16. #include "components/update_client/network.h"
  17. #include "components/update_client/task_traits.h"
  18. #include "components/update_client/update_client_errors.h"
  19. #include "components/update_client/url_fetcher_downloader.h"
  20. #include "components/update_client/utils.h"
  21. namespace update_client {
  22. CrxDownloader::DownloadMetrics::DownloadMetrics()
  23. : downloader(kNone),
  24. error(0),
  25. downloaded_bytes(-1),
  26. total_bytes(-1),
  27. download_time_ms(0) {}
  28. CrxDownloader::CrxDownloader(scoped_refptr<CrxDownloader> successor)
  29. : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
  30. successor_(std::move(successor)) {}
  31. CrxDownloader::~CrxDownloader() = default;
  32. void CrxDownloader::set_progress_callback(
  33. const ProgressCallback& progress_callback) {
  34. progress_callback_ = progress_callback;
  35. }
  36. GURL CrxDownloader::url() const {
  37. return current_url_ != urls_.end() ? *current_url_ : GURL();
  38. }
  39. const std::vector<CrxDownloader::DownloadMetrics>
  40. CrxDownloader::download_metrics() const {
  41. if (!successor_)
  42. return download_metrics_;
  43. std::vector<DownloadMetrics> retval(successor_->download_metrics());
  44. retval.insert(retval.begin(), download_metrics_.begin(),
  45. download_metrics_.end());
  46. return retval;
  47. }
  48. void CrxDownloader::StartDownloadFromUrl(const GURL& url,
  49. const std::string& expected_hash,
  50. DownloadCallback download_callback) {
  51. std::vector<GURL> urls;
  52. urls.push_back(url);
  53. StartDownload(urls, expected_hash, std::move(download_callback));
  54. }
  55. void CrxDownloader::StartDownload(const std::vector<GURL>& urls,
  56. const std::string& expected_hash,
  57. DownloadCallback download_callback) {
  58. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  59. auto error = CrxDownloaderError::NONE;
  60. if (urls.empty()) {
  61. error = CrxDownloaderError::NO_URL;
  62. } else if (expected_hash.empty()) {
  63. error = CrxDownloaderError::NO_HASH;
  64. }
  65. if (error != CrxDownloaderError::NONE) {
  66. Result result;
  67. result.error = static_cast<int>(error);
  68. main_task_runner()->PostTask(
  69. FROM_HERE, base::BindOnce(std::move(download_callback), result));
  70. return;
  71. }
  72. urls_ = urls;
  73. expected_hash_ = expected_hash;
  74. current_url_ = urls_.begin();
  75. download_callback_ = std::move(download_callback);
  76. DoStartDownload(*current_url_);
  77. }
  78. void CrxDownloader::OnDownloadComplete(
  79. bool is_handled,
  80. const Result& result,
  81. const DownloadMetrics& download_metrics) {
  82. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  83. if (result.error) {
  84. main_task_runner()->PostTask(
  85. FROM_HERE, base::BindOnce(&CrxDownloader::HandleDownloadError, this,
  86. is_handled, result, download_metrics));
  87. return;
  88. }
  89. DCHECK_EQ(0, download_metrics.error);
  90. DCHECK(is_handled);
  91. base::ThreadPool::PostTaskAndReplyWithResult(
  92. FROM_HERE, kTaskTraits,
  93. base::BindOnce(
  94. // Verifies the hash of a CRX file. Returns NONE or BAD_HASH if
  95. // the hash of the CRX does not match the |expected_hash|. The input
  96. // file is deleted in case of errors.
  97. [](const base::FilePath& filepath, const std::string& expected_hash) {
  98. if (VerifyFileHash256(filepath, expected_hash))
  99. return CrxDownloaderError::NONE;
  100. DeleteFileAndEmptyParentDirectory(filepath);
  101. return CrxDownloaderError::BAD_HASH;
  102. },
  103. result.response, expected_hash_),
  104. base::BindOnce(
  105. // Handles CRX verification result, and retries the download from
  106. // a different URL if the verification fails.
  107. [](scoped_refptr<CrxDownloader> downloader, Result result,
  108. DownloadMetrics download_metrics, CrxDownloaderError error) {
  109. if (error == CrxDownloaderError::NONE) {
  110. downloader->download_metrics_.push_back(download_metrics);
  111. downloader->main_task_runner()->PostTask(
  112. FROM_HERE,
  113. base::BindOnce(std::move(downloader->download_callback_),
  114. result));
  115. return;
  116. }
  117. result.response.clear();
  118. result.error = static_cast<int>(error);
  119. download_metrics.error = result.error;
  120. downloader->main_task_runner()->PostTask(
  121. FROM_HERE,
  122. base::BindOnce(&CrxDownloader::HandleDownloadError, downloader,
  123. true, result, download_metrics));
  124. },
  125. scoped_refptr<CrxDownloader>(this), result, download_metrics));
  126. }
  127. void CrxDownloader::OnDownloadProgress(int64_t downloaded_bytes,
  128. int64_t total_bytes) {
  129. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  130. if (progress_callback_.is_null())
  131. return;
  132. progress_callback_.Run(downloaded_bytes, total_bytes);
  133. }
  134. void CrxDownloader::HandleDownloadError(
  135. bool is_handled,
  136. const Result& result,
  137. const DownloadMetrics& download_metrics) {
  138. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  139. DCHECK_NE(0, result.error);
  140. DCHECK(result.response.empty());
  141. DCHECK_NE(0, download_metrics.error);
  142. download_metrics_.push_back(download_metrics);
  143. // If an error has occured, try the next url if there is any,
  144. // or try the successor in the chain if there is any successor.
  145. // If this downloader has received a 5xx error for the current url,
  146. // as indicated by the |is_handled| flag, remove that url from the list of
  147. // urls so the url is never tried again down the chain.
  148. if (is_handled) {
  149. current_url_ = urls_.erase(current_url_);
  150. } else {
  151. ++current_url_;
  152. }
  153. // Try downloading from another url from the list.
  154. if (current_url_ != urls_.end()) {
  155. DoStartDownload(*current_url_);
  156. return;
  157. }
  158. // Try downloading using the next downloader.
  159. if (successor_ && !urls_.empty()) {
  160. successor_->StartDownload(urls_, expected_hash_,
  161. std::move(download_callback_));
  162. return;
  163. }
  164. // The download ends here since there is no url nor downloader to handle this
  165. // download request further.
  166. main_task_runner()->PostTask(
  167. FROM_HERE, base::BindOnce(std::move(download_callback_), result));
  168. }
  169. } // namespace update_client