1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/download/internal/background_service/in_memory_download.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "components/download/internal/background_service/blob_task_proxy.h"
12 #include "net/base/load_flags.h"
13 #include "net/traffic_annotation/network_traffic_annotation.h"
14 #include "services/network/public/mojom/url_response_head.mojom.h"
15 #include "storage/browser/blob/blob_data_handle.h"
16 #include "storage/browser/blob/blob_storage_context.h"
17
18 namespace download {
19
InMemoryDownload(const std::string & guid)20 InMemoryDownload::InMemoryDownload(const std::string& guid)
21 : guid_(guid),
22 state_(State::INITIAL),
23 paused_(false),
24 bytes_downloaded_(0u),
25 bytes_uploaded_(0u) {}
26
27 InMemoryDownload::~InMemoryDownload() = default;
28
InMemoryDownloadImpl(const std::string & guid,const RequestParams & request_params,scoped_refptr<network::ResourceRequestBody> request_body,const net::NetworkTrafficAnnotationTag & traffic_annotation,Delegate * delegate,network::mojom::URLLoaderFactory * url_loader_factory,scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)29 InMemoryDownloadImpl::InMemoryDownloadImpl(
30 const std::string& guid,
31 const RequestParams& request_params,
32 scoped_refptr<network::ResourceRequestBody> request_body,
33 const net::NetworkTrafficAnnotationTag& traffic_annotation,
34 Delegate* delegate,
35 network::mojom::URLLoaderFactory* url_loader_factory,
36 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
37 : InMemoryDownload(guid),
38 request_params_(request_params),
39 request_body_(std::move(request_body)),
40 traffic_annotation_(traffic_annotation),
41 url_loader_factory_(url_loader_factory),
42 io_task_runner_(io_task_runner),
43 delegate_(delegate),
44 completion_notified_(false),
45 started_(false) {
46 DCHECK(!guid_.empty());
47 DCHECK(delegate_);
48 }
49
~InMemoryDownloadImpl()50 InMemoryDownloadImpl::~InMemoryDownloadImpl() {
51 io_task_runner_->DeleteSoon(FROM_HERE, blob_task_proxy_.release());
52 }
53
Start()54 void InMemoryDownloadImpl::Start() {
55 DCHECK(state_ == State::INITIAL) << "Only call Start() for new download.";
56 state_ = State::RETRIEVE_BLOB_CONTEXT;
57 delegate_->RetrieveBlobContextGetter(
58 base::BindOnce(&InMemoryDownloadImpl::OnRetrievedBlobContextGetter,
59 weak_ptr_factory_.GetWeakPtr()));
60 }
61
OnRetrievedBlobContextGetter(BlobContextGetter blob_context_getter)62 void InMemoryDownloadImpl::OnRetrievedBlobContextGetter(
63 BlobContextGetter blob_context_getter) {
64 DCHECK(state_ == State::RETRIEVE_BLOB_CONTEXT);
65 blob_task_proxy_ =
66 BlobTaskProxy::Create(blob_context_getter, io_task_runner_);
67 SendRequest();
68 state_ = State::IN_PROGRESS;
69 }
70
Pause()71 void InMemoryDownloadImpl::Pause() {
72 if (state_ == State::IN_PROGRESS)
73 paused_ = true;
74 }
75
Resume()76 void InMemoryDownloadImpl::Resume() {
77 paused_ = false;
78
79 switch (state_) {
80 case State::INITIAL:
81 case State::RETRIEVE_BLOB_CONTEXT:
82 return;
83 case State::IN_PROGRESS:
84 // Let the network pipe continue to read data.
85 if (resume_callback_)
86 std::move(resume_callback_).Run();
87 return;
88 case State::FAILED:
89 // Restart the download.
90 Reset();
91 SendRequest();
92 state_ = State::IN_PROGRESS;
93 return;
94 case State::COMPLETE:
95 NotifyDelegateDownloadComplete();
96 return;
97 }
98 }
99
ResultAsBlob() const100 std::unique_ptr<storage::BlobDataHandle> InMemoryDownloadImpl::ResultAsBlob()
101 const {
102 DCHECK(state_ == State::COMPLETE || state_ == State::FAILED);
103 // Return a copy.
104 return std::make_unique<storage::BlobDataHandle>(*blob_data_handle_);
105 }
106
EstimateMemoryUsage() const107 size_t InMemoryDownloadImpl::EstimateMemoryUsage() const {
108 return bytes_downloaded_;
109 }
110
OnDataReceived(base::StringPiece string_piece,base::OnceClosure resume)111 void InMemoryDownloadImpl::OnDataReceived(base::StringPiece string_piece,
112 base::OnceClosure resume) {
113 size_t size = string_piece.as_string().size();
114 data_.append(string_piece.as_string().data(), size);
115 bytes_downloaded_ += size;
116
117 if (paused_) {
118 // Read data later and cache the resumption callback when paused.
119 resume_callback_ = std::move(resume);
120 return;
121 }
122
123 // Continue to read data.
124 std::move(resume).Run();
125
126 // TODO(xingliu): Throttle the update frequency. See https://crbug.com/809674.
127 delegate_->OnDownloadProgress(this);
128 }
129
OnComplete(bool success)130 void InMemoryDownloadImpl::OnComplete(bool success) {
131 if (success) {
132 SaveAsBlob();
133 return;
134 }
135
136 state_ = State::FAILED;
137
138 // Release download data.
139 data_.clear();
140
141 // OnComplete() called without OnResponseStarted(). This will happen when the
142 // request was aborted.
143 if (!started_)
144 OnResponseStarted(GURL(), network::mojom::URLResponseHead());
145
146 NotifyDelegateDownloadComplete();
147 }
148
OnRetry(base::OnceClosure start_retry)149 void InMemoryDownloadImpl::OnRetry(base::OnceClosure start_retry) {
150 Reset();
151
152 // The original URL is recorded in this class instead of |loader_|, so when
153 // running retry closure from SimpleUrlLoader, add back the original URL.
154 url_chain_.push_back(request_params_.url);
155
156 std::move(start_retry).Run();
157 }
158
SaveAsBlob()159 void InMemoryDownloadImpl::SaveAsBlob() {
160 auto callback = base::BindOnce(&InMemoryDownloadImpl::OnSaveBlobDone,
161 weak_ptr_factory_.GetWeakPtr());
162 auto data = std::make_unique<std::string>(std::move(data_));
163 blob_task_proxy_->SaveAsBlob(std::move(data), std::move(callback));
164 }
165
OnSaveBlobDone(std::unique_ptr<storage::BlobDataHandle> blob_handle,storage::BlobStatus status)166 void InMemoryDownloadImpl::OnSaveBlobDone(
167 std::unique_ptr<storage::BlobDataHandle> blob_handle,
168 storage::BlobStatus status) {
169 // |status| is valid on IO thread, consumer of |blob_handle| should validate
170 // the data when using the blob data.
171 state_ =
172 (status == storage::BlobStatus::DONE) ? State::COMPLETE : State::FAILED;
173
174 // TODO(xingliu): Add metric for blob status code. If failed, consider remove
175 // |blob_data_handle_|. See https://crbug.com/809674.
176 DCHECK(data_.empty())
177 << "Download data should be contained in |blob_data_handle_|.";
178 blob_data_handle_ = std::move(blob_handle);
179 completion_time_ = base::Time::Now();
180
181 // Resets network backend.
182 loader_.reset();
183
184 // Not considering |paused_| here, if pause after starting a blob operation,
185 // just let it finish.
186 NotifyDelegateDownloadComplete();
187 }
188
NotifyDelegateDownloadComplete()189 void InMemoryDownloadImpl::NotifyDelegateDownloadComplete() {
190 if (completion_notified_)
191 return;
192 completion_notified_ = true;
193
194 delegate_->OnDownloadComplete(this);
195 }
196
SendRequest()197 void InMemoryDownloadImpl::SendRequest() {
198 auto request = std::make_unique<network::ResourceRequest>();
199 request->url = request_params_.url;
200 request->method = request_params_.method;
201 request->headers = request_params_.request_headers;
202 request->load_flags = net::LOAD_DISABLE_CACHE;
203 if (request_body_) {
204 request->request_body = std::move(request_body_);
205 request->enable_upload_progress = true;
206 }
207
208 url_chain_.push_back(request_params_.url);
209
210 loader_ =
211 network::SimpleURLLoader::Create(std::move(request), traffic_annotation_);
212 loader_->SetOnRedirectCallback(base::BindRepeating(
213 &InMemoryDownloadImpl::OnRedirect, weak_ptr_factory_.GetWeakPtr()));
214 loader_->SetOnResponseStartedCallback(
215 base::BindRepeating(&InMemoryDownloadImpl::OnResponseStarted,
216 weak_ptr_factory_.GetWeakPtr()));
217 loader_->SetOnUploadProgressCallback(base::BindRepeating(
218 &InMemoryDownloadImpl::OnUploadProgress, weak_ptr_factory_.GetWeakPtr()));
219
220 // TODO(xingliu): Use SimpleURLLoader's retry when it won't hit CHECK in
221 // SharedURLLoaderFactory.
222 loader_->DownloadAsStream(url_loader_factory_, this);
223 }
224
OnRedirect(const net::RedirectInfo & redirect_info,const network::mojom::URLResponseHead & response_head,std::vector<std::string> * to_be_removed_headers)225 void InMemoryDownloadImpl::OnRedirect(
226 const net::RedirectInfo& redirect_info,
227 const network::mojom::URLResponseHead& response_head,
228 std::vector<std::string>* to_be_removed_headers) {
229 url_chain_.push_back(redirect_info.new_url);
230 }
231
OnResponseStarted(const GURL & final_url,const network::mojom::URLResponseHead & response_head)232 void InMemoryDownloadImpl::OnResponseStarted(
233 const GURL& final_url,
234 const network::mojom::URLResponseHead& response_head) {
235 started_ = true;
236 response_headers_ = response_head.headers;
237
238 delegate_->OnDownloadStarted(this);
239 }
240
OnUploadProgress(uint64_t position,uint64_t total)241 void InMemoryDownloadImpl::OnUploadProgress(uint64_t position, uint64_t total) {
242 bytes_uploaded_ = position;
243 delegate_->OnUploadProgress(this);
244 }
245
Reset()246 void InMemoryDownloadImpl::Reset() {
247 data_.clear();
248 url_chain_.clear();
249 response_headers_.reset();
250 bytes_downloaded_ = 0u;
251 completion_notified_ = false;
252 started_ = false;
253 resume_callback_.Reset();
254 }
255
256 } // namespace download
257