File: download_job_factory.cc

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (135 lines) | stat: -rw-r--r-- 5,447 bytes parent folder | download | duplicates (6)
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