blob: ad808ce9f28b3e30ad11e107fed2818b97abf7e6 [file] [log] [blame]
// 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/url_fetcher_downloader.h"
#include <stdint.h>
#include <utility>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/sequenced_task_runner.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "components/update_client/network.h"
#include "components/update_client/utils.h"
#include "url/gurl.h"
namespace {
constexpr base::TaskTraits kTaskTraits = {
base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
} // namespace
namespace update_client {
UrlFetcherDownloader::UrlFetcherDownloader(
std::unique_ptr<CrxDownloader> successor,
scoped_refptr<NetworkFetcherFactory> network_fetcher_factory)
: CrxDownloader(std::move(successor)),
network_fetcher_factory_(network_fetcher_factory) {}
UrlFetcherDownloader::~UrlFetcherDownloader() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void UrlFetcherDownloader::DoStartDownload(const GURL& url) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
base::PostTaskAndReply(
FROM_HERE, kTaskTraits,
base::BindOnce(&UrlFetcherDownloader::CreateDownloadDir,
base::Unretained(this)),
base::BindOnce(&UrlFetcherDownloader::StartURLFetch,
base::Unretained(this), url));
}
void UrlFetcherDownloader::CreateDownloadDir() {
base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_url_fetcher_"),
&download_dir_);
}
void UrlFetcherDownloader::StartURLFetch(const GURL& url) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (download_dir_.empty()) {
Result result;
result.error = -1;
DownloadMetrics download_metrics;
download_metrics.url = url;
download_metrics.downloader = DownloadMetrics::kUrlFetcher;
download_metrics.error = -1;
download_metrics.downloaded_bytes = -1;
download_metrics.total_bytes = -1;
download_metrics.download_time_ms = 0;
main_task_runner()->PostTask(
FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete,
base::Unretained(this), false, result,
download_metrics));
return;
}
file_path_ = download_dir_.AppendASCII(url.ExtractFileName());
network_fetcher_ = network_fetcher_factory_->Create();
network_fetcher_->DownloadToFile(
url, file_path_,
base::BindOnce(&UrlFetcherDownloader::OnResponseStarted,
base::Unretained(this)),
base::BindRepeating(&UrlFetcherDownloader::OnDownloadProgress,
base::Unretained(this)),
base::BindOnce(&UrlFetcherDownloader::OnNetworkFetcherComplete,
base::Unretained(this)));
download_start_time_ = base::TimeTicks::Now();
}
void UrlFetcherDownloader::OnNetworkFetcherComplete(int net_error,
int64_t content_size) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
const base::TimeTicks download_end_time(base::TimeTicks::Now());
const base::TimeDelta download_time =
download_end_time >= download_start_time_
? download_end_time - download_start_time_
: base::TimeDelta();
// Consider a 5xx response from the server as an indication to terminate
// the request and avoid overloading the server in this case.
// is not accepting requests for the moment.
int error = -1;
if (!net_error && response_code_ == 200)
error = 0;
else if (response_code_ != -1)
error = response_code_;
else
error = net_error;
const bool is_handled = error == 0 || IsHttpServerError(error);
Result result;
result.error = error;
if (!error)
result.response = file_path_;
DownloadMetrics download_metrics;
download_metrics.url = url();
download_metrics.downloader = DownloadMetrics::kUrlFetcher;
download_metrics.error = error;
// Tests expected -1, in case of failures and no content is available.
download_metrics.downloaded_bytes = error ? -1 : content_size;
download_metrics.total_bytes = total_bytes_;
download_metrics.download_time_ms = download_time.InMilliseconds();
VLOG(1) << "Downloaded " << content_size << " bytes in "
<< download_time.InMilliseconds() << "ms from " << url().spec()
<< " to " << result.response.value();
// Delete the download directory in the error cases.
if (error && !download_dir_.empty()) {
base::PostTask(FROM_HERE, kTaskTraits,
base::BindOnce(IgnoreResult(&base::DeleteFileRecursively),
download_dir_));
}
main_task_runner()->PostTask(
FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete,
base::Unretained(this), is_handled, result,
download_metrics));
}
// This callback is used to indicate that a download has been started.
void UrlFetcherDownloader::OnResponseStarted(int response_code,
int64_t content_length) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
VLOG(1) << "url fetcher response started for: " << url().spec();
response_code_ = response_code;
total_bytes_ = content_length;
}
void UrlFetcherDownloader::OnDownloadProgress(int64_t current) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
CrxDownloader::OnDownloadProgress();
}
} // namespace update_client