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
5 #include "components/update_client/url_fetcher_downloader.h"
6
7 #include <stdint.h>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/files/file_util.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/task/thread_pool.h"
16 #include "components/update_client/network.h"
17 #include "components/update_client/task_traits.h"
18 #include "components/update_client/utils.h"
19 #include "url/gurl.h"
20
21 namespace update_client {
22
UrlFetcherDownloader(scoped_refptr<CrxDownloader> successor,scoped_refptr<NetworkFetcherFactory> network_fetcher_factory)23 UrlFetcherDownloader::UrlFetcherDownloader(
24 scoped_refptr<CrxDownloader> successor,
25 scoped_refptr<NetworkFetcherFactory> network_fetcher_factory)
26 : CrxDownloader(std::move(successor)),
27 network_fetcher_factory_(network_fetcher_factory) {}
28
29 UrlFetcherDownloader::~UrlFetcherDownloader() = default;
30
DoStartDownload(const GURL & url)31 void UrlFetcherDownloader::DoStartDownload(const GURL& url) {
32 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
33 base::ThreadPool::PostTaskAndReply(
34 FROM_HERE, kTaskTraits,
35 base::BindOnce(&UrlFetcherDownloader::CreateDownloadDir, this),
36 base::BindOnce(&UrlFetcherDownloader::StartURLFetch, this, url));
37 }
38
CreateDownloadDir()39 void UrlFetcherDownloader::CreateDownloadDir() {
40 base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_url_fetcher_"),
41 &download_dir_);
42 }
43
StartURLFetch(const GURL & url)44 void UrlFetcherDownloader::StartURLFetch(const GURL& url) {
45 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
46
47 if (download_dir_.empty()) {
48 Result result;
49 result.error = -1;
50
51 DownloadMetrics download_metrics;
52 download_metrics.url = url;
53 download_metrics.downloader = DownloadMetrics::kUrlFetcher;
54 download_metrics.error = -1;
55 download_metrics.downloaded_bytes = -1;
56 download_metrics.total_bytes = -1;
57 download_metrics.download_time_ms = 0;
58
59 main_task_runner()->PostTask(
60 FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete,
61 this, false, result, download_metrics));
62 return;
63 }
64
65 file_path_ = download_dir_.AppendASCII(url.ExtractFileName());
66 network_fetcher_ = network_fetcher_factory_->Create();
67 network_fetcher_->DownloadToFile(
68 url, file_path_,
69 base::BindOnce(&UrlFetcherDownloader::OnResponseStarted, this),
70 base::BindRepeating(&UrlFetcherDownloader::OnDownloadProgress, this),
71 base::BindOnce(&UrlFetcherDownloader::OnNetworkFetcherComplete, this));
72
73 download_start_time_ = base::TimeTicks::Now();
74 }
75
OnNetworkFetcherComplete(int net_error,int64_t content_size)76 void UrlFetcherDownloader::OnNetworkFetcherComplete(int net_error,
77 int64_t content_size) {
78 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
79
80 const base::TimeTicks download_end_time(base::TimeTicks::Now());
81 const base::TimeDelta download_time =
82 download_end_time >= download_start_time_
83 ? download_end_time - download_start_time_
84 : base::TimeDelta();
85
86 // Consider a 5xx response from the server as an indication to terminate
87 // the request and avoid overloading the server in this case.
88 // is not accepting requests for the moment.
89 int error = -1;
90 if (!net_error && response_code_ == 200)
91 error = 0;
92 else if (response_code_ != -1)
93 error = response_code_;
94 else
95 error = net_error;
96
97 const bool is_handled = error == 0 || IsHttpServerError(error);
98
99 Result result;
100 result.error = error;
101 if (!error)
102 result.response = file_path_;
103
104 DownloadMetrics download_metrics;
105 download_metrics.url = url();
106 download_metrics.downloader = DownloadMetrics::kUrlFetcher;
107 download_metrics.error = error;
108 // Tests expected -1, in case of failures and no content is available.
109 download_metrics.downloaded_bytes = error ? -1 : content_size;
110 download_metrics.total_bytes = total_bytes_;
111 download_metrics.download_time_ms = download_time.InMilliseconds();
112
113 VLOG(1) << "Downloaded " << content_size << " bytes in "
114 << download_time.InMilliseconds() << "ms from " << url().spec()
115 << " to " << result.response.value();
116
117 // Delete the download directory in the error cases.
118 if (error && !download_dir_.empty()) {
119 base::ThreadPool::PostTask(
120 FROM_HERE, kTaskTraits,
121 base::BindOnce(IgnoreResult(&base::DeletePathRecursively),
122 download_dir_));
123 }
124
125 main_task_runner()->PostTask(
126 FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete, this,
127 is_handled, result, download_metrics));
128 network_fetcher_ = nullptr;
129 }
130
131 // This callback is used to indicate that a download has been started.
OnResponseStarted(int response_code,int64_t content_length)132 void UrlFetcherDownloader::OnResponseStarted(int response_code,
133 int64_t content_length) {
134 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
135
136 VLOG(1) << "url fetcher response started for: " << url().spec();
137
138 response_code_ = response_code;
139 total_bytes_ = content_length;
140 }
141
OnDownloadProgress(int64_t current)142 void UrlFetcherDownloader::OnDownloadProgress(int64_t current) {
143 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
144 CrxDownloader::OnDownloadProgress(current, total_bytes_);
145 }
146
147 } // namespace update_client
148