blob: db41162941fe08683e480a3ef496077e6d9884bc [file] [log] [blame]
// Copyright 2017 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/download/public/common/download_job_factory.h"
#include <memory>
#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 "components/download/public/common/download_stats.h"
#include "components/download/public/common/download_url_loader_factory_getter.h"
#include "net/url_request/url_request_context_getter.h"
namespace download {
namespace {
// Connection type of the download.
enum class ConnectionType {
kHTTP = 0,
kHTTP2,
kQUIC,
kUnknown,
};
ConnectionType GetConnectionType(
net::HttpResponseInfo::ConnectionInfo connection_info) {
switch (connection_info) {
case net::HttpResponseInfo::CONNECTION_INFO_HTTP0_9:
case net::HttpResponseInfo::CONNECTION_INFO_HTTP1_0:
case net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1:
return ConnectionType::kHTTP;
case net::HttpResponseInfo::CONNECTION_INFO_DEPRECATED_SPDY2:
case net::HttpResponseInfo::CONNECTION_INFO_DEPRECATED_SPDY3:
case net::HttpResponseInfo::CONNECTION_INFO_DEPRECATED_HTTP2_14:
case net::HttpResponseInfo::CONNECTION_INFO_DEPRECATED_HTTP2_15:
case net::HttpResponseInfo::CONNECTION_INFO_HTTP2:
return ConnectionType::kHTTP2;
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_UNKNOWN_VERSION:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_32:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_33:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_34:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_35:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_36:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_37:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_38:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_39:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_40:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_41:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_42:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_43:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_44:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_45:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_46:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_47:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_48:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_99:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_999:
return ConnectionType::kQUIC;
case net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN:
return ConnectionType::kUnknown;
case net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS:
NOTREACHED();
return ConnectionType::kUnknown;
}
NOTREACHED();
return ConnectionType::kUnknown;
}
// Returns if the download can be parallelized.
bool IsParallelizableDownload(const DownloadCreateInfo& create_info,
DownloadItem* download_item) {
// 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::HttpResponseInfo::CONNECTION_INFO_HTTP1_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();
bool partial_response_success =
download_item->GetReceivedSlices().empty() || create_info.offset != 0;
bool range_support_allowed =
create_info.accept_range == RangeRequestSupportType::kSupport ||
(base::FeatureList::IsEnabled(
features::kUseParallelRequestsForUnknwonRangeSupport) &&
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 &&
partial_response_success;
RecordDownloadConnectionInfo(create_info.connection_info);
if (!IsParallelDownloadEnabled())
return is_parallelizable;
RecordParallelDownloadCreationEvent(
is_parallelizable
? ParallelDownloadCreationEvent::STARTED_PARALLEL_DOWNLOAD
: ParallelDownloadCreationEvent::FELL_BACK_TO_NORMAL_DOWNLOAD);
if (!has_strong_validator) {
RecordParallelDownloadCreationEvent(
ParallelDownloadCreationEvent::FALLBACK_REASON_STRONG_VALIDATORS);
}
if (!range_support_allowed) {
RecordParallelDownloadCreationEvent(
ParallelDownloadCreationEvent::FALLBACK_REASON_ACCEPT_RANGE_HEADER);
if (create_info.accept_range == RangeRequestSupportType::kUnknown) {
RecordParallelDownloadCreationEvent(
ParallelDownloadCreationEvent::FALLBACK_REASON_UNKNOWN_RANGE_SUPPORT);
}
}
if (!has_content_length) {
RecordParallelDownloadCreationEvent(
ParallelDownloadCreationEvent::FALLBACK_REASON_CONTENT_LENGTH_HEADER);
}
if (!satisfy_min_file_size) {
RecordParallelDownloadCreationEvent(
ParallelDownloadCreationEvent::FALLBACK_REASON_FILE_SIZE);
}
if (!satisfy_connection_type) {
RecordParallelDownloadCreationEvent(
ParallelDownloadCreationEvent::FALLBACK_REASON_CONNECTION_TYPE);
}
if (!http_get_method) {
RecordParallelDownloadCreationEvent(
ParallelDownloadCreationEvent::FALLBACK_REASON_HTTP_METHOD);
}
return is_parallelizable;
}
} // namespace
// static
std::unique_ptr<DownloadJob> DownloadJobFactory::CreateJob(
DownloadItem* download_item,
std::unique_ptr<DownloadRequestHandleInterface> req_handle,
const DownloadCreateInfo& create_info,
bool is_save_package_download,
scoped_refptr<download::DownloadURLLoaderFactoryGetter>
url_loader_factory_getter,
net::URLRequestContextGetter* url_request_context_getter,
service_manager::Connector* connector) {
if (is_save_package_download) {
return std::make_unique<SavePackageDownloadJob>(download_item,
std::move(req_handle));
}
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(req_handle), create_info,
std::move(url_loader_factory_getter), url_request_context_getter,
connector);
}
// An ordinary download job.
return std::make_unique<DownloadJobImpl>(download_item, std::move(req_handle),
is_parallelizable);
}
} // namespace download