summaryrefslogtreecommitdiffstats
path: root/components/update_client/crx_downloader.cc
blob: ecbf301d8bd0a00cc57eed2300b16cf288b59bd4 (plain)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// 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/crx_downloader.h"

#include "base/logging.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "components/update_client/url_fetcher_downloader.h"

#if defined(OS_WIN)
#include "components/update_client/background_downloader_win.h"
#endif

namespace update_client {

CrxDownloader::Result::Result()
    : error(0), downloaded_bytes(-1), total_bytes(-1) {
}

CrxDownloader::DownloadMetrics::DownloadMetrics()
    : downloader(kNone),
      error(0),
      downloaded_bytes(-1),
      total_bytes(-1),
      download_time_ms(0) {
}

// On Windows, the first downloader in the chain is a background downloader,
// which uses the BITS service.
CrxDownloader* CrxDownloader::Create(
    bool is_background_download,
    net::URLRequestContextGetter* context_getter,
    scoped_refptr<base::SequencedTaskRunner> url_fetcher_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) {
  scoped_ptr<CrxDownloader> url_fetcher_downloader(
      new UrlFetcherDownloader(scoped_ptr<CrxDownloader>().Pass(),
                               context_getter, url_fetcher_task_runner));
#if defined(OS_WIN)
  if (is_background_download) {
    return new BackgroundDownloader(url_fetcher_downloader.Pass(),
                                    context_getter, background_task_runner);
  }
#endif

  return url_fetcher_downloader.release();
}

CrxDownloader::CrxDownloader(scoped_ptr<CrxDownloader> successor)
    : successor_(successor.Pass()) {
}

CrxDownloader::~CrxDownloader() {
}

void CrxDownloader::set_progress_callback(
    const ProgressCallback& progress_callback) {
  progress_callback_ = progress_callback;
}

GURL CrxDownloader::url() const {
  return current_url_ != urls_.end() ? *current_url_ : GURL();
}

const std::vector<CrxDownloader::DownloadMetrics>
CrxDownloader::download_metrics() const {
  if (!successor_)
    return download_metrics_;

  std::vector<DownloadMetrics> retval(successor_->download_metrics());
  retval.insert(retval.begin(), download_metrics_.begin(),
                download_metrics_.end());
  return retval;
}

void CrxDownloader::StartDownloadFromUrl(
    const GURL& url,
    const DownloadCallback& download_callback) {
  std::vector<GURL> urls;
  urls.push_back(url);
  StartDownload(urls, download_callback);
}

void CrxDownloader::StartDownload(const std::vector<GURL>& urls,
                                  const DownloadCallback& download_callback) {
  DCHECK(thread_checker_.CalledOnValidThread());

  if (urls.empty()) {
    // Make a result and complete the download with a generic error for now.
    Result result;
    result.error = -1;
    download_callback.Run(result);
    return;
  }

  // If the urls are mutated while this downloader is active, then the
  // behavior is undefined in the sense that the outcome of the download could
  // be inconsistent for the list of urls. At any rate, the |current_url_| is
  // reset at this point, and the iterator will be valid in all conditions.
  urls_ = urls;
  current_url_ = urls_.begin();
  download_callback_ = download_callback;

  DoStartDownload(*current_url_);
}

void CrxDownloader::OnDownloadComplete(
    bool is_handled,
    const Result& result,
    const DownloadMetrics& download_metrics) {
  DCHECK(thread_checker_.CalledOnValidThread());

  download_metrics_.push_back(download_metrics);

  if (result.error) {
    // If an error has occured, in general try the next url if there is any,
    // then move on to the successor in the chain if there is any successor.
    // If this downloader has received a 5xx error for the current url,
    // as indicated by the |is_handled| flag, remove that url from the list of
    // urls so the url is never retried. In both cases, move on to the
    // next url.
    if (!is_handled) {
      ++current_url_;
    } else {
      current_url_ = urls_.erase(current_url_);
    }

    // Try downloading from another url from the list.
    if (current_url_ != urls_.end()) {
      DoStartDownload(*current_url_);
      return;
    }

    // If there is another downloader that can accept this request, then hand
    // the request over to it so that the successor can try the pruned list
    // of urls. Otherwise, the request ends here since the current downloader
    // has tried all urls and it can't fall back on any other downloader.
    if (successor_ && !urls_.empty()) {
      successor_->StartDownload(urls_, download_callback_);
      return;
    }
  }

  download_callback_.Run(result);
}

void CrxDownloader::OnDownloadProgress(const Result& result) {
  DCHECK(thread_checker_.CalledOnValidThread());

  if (progress_callback_.is_null())
    return;

  progress_callback_.Run(result);
}

}  // namespace update_client