1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/download/public/common/download_job_factory.h"
#include <memory>
#include "base/memory/weak_ptr.h"
#include "components/download/internal/common/download_job_impl.h"
#include "components/download/internal/common/parallel_download_job.h"
#include "components/download/internal/common/parallel_download_utils.h"
#include "components/download/internal/common/save_package_download_job.h"
#include "components/download/public/common/download_features.h"
#include "components/download/public/common/download_item.h"
#include "net/http/http_connection_info.h"
namespace download {
namespace {
// Connection type of the download.
enum class ConnectionType {
kHTTP = 0,
kHTTP2,
kQUIC,
kUnknown,
};
ConnectionType GetConnectionType(net::HttpConnectionInfo connection_info) {
switch (net::HttpConnectionInfoToCoarse(connection_info)) {
case net::HttpConnectionInfoCoarse::kHTTP1:
return ConnectionType::kHTTP;
case net::HttpConnectionInfoCoarse::kHTTP2:
return ConnectionType::kHTTP2;
case net::HttpConnectionInfoCoarse::kQUIC:
return ConnectionType::kQUIC;
case net::HttpConnectionInfoCoarse::kOTHER:
return ConnectionType::kUnknown;
}
NOTREACHED();
}
// Returns if the download can be parallelized.
bool IsParallelizableDownload(const DownloadCreateInfo& create_info,
DownloadItem* download_item) {
if (download_item->GetDownloadFile() &&
download_item->GetDownloadFile()->IsMemoryFile()) {
return false;
}
// To enable parallel download, following conditions need to be satisfied.
// 1. Feature |kParallelDownloading| enabled.
// 2. Strong validators response headers. i.e. ETag and Last-Modified.
// 3. Accept-Ranges or Content-Range header.
// 4. Content-Length header.
// 5. Content-Length is no less than the minimum slice size configuration, or
// persisted slices alreay exist.
// 6. HTTP/1.1 protocol, not QUIC nor HTTP/1.0.
// 7. HTTP or HTTPS scheme with GET method in the initial request.
// Etag and last modified are stored into DownloadCreateInfo in
// DownloadRequestCore only if the response header complies to the strong
// validator rule.
bool has_strong_validator =
!create_info.etag.empty() || !create_info.last_modified.empty();
bool has_content_length = create_info.total_bytes > 0;
bool satisfy_min_file_size =
!download_item->GetReceivedSlices().empty() ||
create_info.total_bytes >= GetMinSliceSizeConfig();
ConnectionType type = GetConnectionType(create_info.connection_info);
bool satisfy_connection_type =
(create_info.connection_info == net::HttpConnectionInfo::kHTTP1_1) ||
(type == ConnectionType::kHTTP2 &&
base::FeatureList::IsEnabled(features::kUseParallelRequestsForHTTP2)) ||
(type == ConnectionType::kQUIC &&
base::FeatureList::IsEnabled(features::kUseParallelRequestsForQUIC));
bool http_get_method =
create_info.method == "GET" && create_info.url().SchemeIsHTTPOrHTTPS();
// If the file is empty, we always assume parallel download is supported.
// Otherwise, check if the download already has multiple slices and whether
// the http response offset is non-zero.
bool can_support_parallel_requests =
download_item->GetReceivedBytes() <= 0 ||
(download_item->GetReceivedSlices().size() > 0 &&
create_info.offset != 0);
bool range_support_allowed =
create_info.accept_range == RangeRequestSupportType::kSupport ||
create_info.accept_range == RangeRequestSupportType::kUnknown;
bool is_parallelizable = has_strong_validator && range_support_allowed &&
has_content_length && satisfy_min_file_size &&
satisfy_connection_type && http_get_method &&
can_support_parallel_requests;
// Don't use parallel download if the caller wants to fetch with range
// request explicitly.
bool arbitrary_range_request =
create_info.save_info && create_info.save_info->IsArbitraryRangeRequest();
is_parallelizable = is_parallelizable && !arbitrary_range_request;
return is_parallelizable;
}
} // namespace
// static
std::unique_ptr<DownloadJob> DownloadJobFactory::CreateJob(
DownloadItem* download_item,
DownloadJob::CancelRequestCallback cancel_request_callback,
const DownloadCreateInfo& create_info,
bool is_save_package_download,
URLLoaderFactoryProvider::URLLoaderFactoryProviderPtr
url_loader_factory_provider,
WakeLockProviderBinder wake_lock_provider_binder) {
if (is_save_package_download) {
return std::make_unique<SavePackageDownloadJob>(
download_item, std::move(cancel_request_callback));
}
bool is_parallelizable = IsParallelizableDownload(create_info, download_item);
// Build parallel download job.
if (IsParallelDownloadEnabled() && is_parallelizable) {
return std::make_unique<ParallelDownloadJob>(
download_item, std::move(cancel_request_callback), create_info,
std::move(url_loader_factory_provider),
std::move(wake_lock_provider_binder));
}
// An ordinary download job.
return std::make_unique<DownloadJobImpl>(
download_item, std::move(cancel_request_callback), is_parallelizable);
}
} // namespace download
|