Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(242)

Side by Side Diff: content/browser/download/url_downloader.cc

Issue 1418663010: Adding WebContent-free Download (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixing Mock for GetBrowserContext. Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 "content/browser/download/url_downloader.h"
6
7 #include "base/location.h"
8 #include "base/thread_task_runner_handle.h"
9 #include "content/browser/appcache/appcache_interceptor.h"
10 #include "content/browser/download/download_resource_handler.h"
11 #include "content/browser/loader/resource_request_info_impl.h"
12 #include "content/browser/service_worker/service_worker_request_handler.h"
13 #include "content/browser/ssl/ssl_policy.h"
14 #include "content/common/ssl_status_serialization.h"
15 #include "content/public/browser/cert_store.h"
16 #include "content/public/browser/download_save_info.h"
17 #include "content/public/browser/resource_context.h"
18 #include "content/public/browser/signed_certificate_timestamp_store.h"
19 #include "content/public/common/process_type.h"
20 #include "content/public/common/resource_response.h"
21 #include "content/public/common/security_style.h"
22 #include "net/base/io_buffer.h"
23 #include "net/base/load_flags.h"
24 #include "net/http/http_response_headers.h"
25 #include "net/http/http_status_code.h"
26 #include "ui/base/page_transition_types.h"
27
28 namespace content {
29 // The average private bytes increase of the browser for each new pending
30 // request. Experimentally obtained.
31 static const int kAvgBytesPerOutstandingRequest = 4400;
32
33 int CalculateApproximateMemoryCost(net::URLRequest* request) {
34 // The following fields should be a minor size contribution (experimentally
35 // on the order of 100). However since they are variable length, it could
36 // in theory be a sizeable contribution.
37 int strings_cost = request->extra_request_headers().ToString().size() +
38 request->original_url().spec().size() +
39 request->referrer().size() + request->method().size();
40
41 // Note that this expression will typically be dominated by:
42 // |kAvgBytesPerOutstandingRequest|.
43 return kAvgBytesPerOutstandingRequest + strings_cost;
44 }
45
46 void StoreSignedCertificateTimestamps(
47 const net::SignedCertificateTimestampAndStatusList& sct_list,
48 int process_id,
49 SignedCertificateTimestampIDStatusList* sct_ids) {
50 SignedCertificateTimestampStore* sct_store(
51 SignedCertificateTimestampStore::GetInstance());
52
53 for (auto iter = sct_list.begin(); iter != sct_list.end(); ++iter) {
54 const int sct_id(sct_store->Store(iter->sct.get(), process_id));
55 sct_ids->push_back(
56 SignedCertificateTimestampIDAndStatus(sct_id, iter->status));
57 }
58 }
59
60 void GetSSLStatusForRequest(const GURL& url,
61 const net::SSLInfo& ssl_info,
62 int child_id,
63 SSLStatus* ssl_status) {
64 DCHECK(ssl_info.cert);
65
66 int cert_id =
67 CertStore::GetInstance()->StoreCert(ssl_info.cert.get(), child_id);
68
69 SignedCertificateTimestampIDStatusList signed_certificate_timestamp_ids;
70 StoreSignedCertificateTimestamps(ssl_info.signed_certificate_timestamps,
71 child_id, &signed_certificate_timestamp_ids);
72
73 *ssl_status = SSLStatus(SSLPolicy::GetSecurityStyleForResource(
74 url, cert_id, ssl_info.cert_status),
75 cert_id, signed_certificate_timestamp_ids, ssl_info);
76 }
77
78 void PopulateResourceResponse(ResourceRequestInfoImpl* info,
79 net::URLRequest* request,
80 ResourceResponse* response) {
81 response->head.request_time = request->request_time();
82 response->head.response_time = request->response_time();
83 response->head.headers = request->response_headers();
84 request->GetCharset(&response->head.charset);
85 response->head.content_length = request->GetExpectedContentSize();
86 request->GetMimeType(&response->head.mime_type);
87 net::HttpResponseInfo response_info = request->response_info();
88 response->head.was_fetched_via_spdy = response_info.was_fetched_via_spdy;
89 response->head.was_npn_negotiated = response_info.was_npn_negotiated;
90 response->head.npn_negotiated_protocol =
91 response_info.npn_negotiated_protocol;
92 response->head.connection_info = response_info.connection_info;
93 response->head.was_fetched_via_proxy = request->was_fetched_via_proxy();
94 response->head.proxy_server = response_info.proxy_server;
95 response->head.socket_address = request->GetSocketAddress();
96 const content::ResourceRequestInfo* request_info =
97 content::ResourceRequestInfo::ForRequest(request);
98 if (request_info)
99 response->head.is_using_lofi = request_info->IsUsingLoFi();
100 if (ServiceWorkerRequestHandler* handler =
101 ServiceWorkerRequestHandler::GetHandler(request)) {
102 handler->GetExtraResponseInfo(&response->head);
103 }
104 AppCacheInterceptor::GetExtraResponseInfo(
105 request, &response->head.appcache_id,
106 &response->head.appcache_manifest_url);
107 if (info->is_load_timing_enabled())
108 request->GetLoadTimingInfo(&response->head.load_timing);
109
110 if (request->ssl_info().cert.get()) {
111 SSLStatus ssl_status;
112 GetSSLStatusForRequest(request->url(), request->ssl_info(),
113 info->GetChildID(), &ssl_status);
114 response->head.security_info = SerializeSecurityInfo(ssl_status);
115 } else {
116 // We should not have any SSL state.
117 DCHECK(!request->ssl_info().cert_status);
118 DCHECK_EQ(request->ssl_info().security_bits, -1);
119 DCHECK_EQ(request->ssl_info().key_exchange_info, 0);
120 DCHECK(!request->ssl_info().connection_status);
121 }
122 }
123
124 DownloadInterruptReason UrlDownloader::BeginDownload(
125 base::WeakPtr<DownloadManagerImpl> download_manager,
126 scoped_ptr<net::URLRequest> request,
127 const Referrer& referrer,
128 bool is_content_initiated,
129 ResourceContext* context,
130 bool prefer_cache,
131 bool do_not_prompt_for_login,
132 scoped_ptr<DownloadSaveInfo> save_info,
133 uint32 download_id,
134 const DownloadUrlParameters::OnStartedCallback& started_callback) {
135 if (!referrer.url.is_valid())
136 request->SetReferrer(std::string());
137 else
138 request->SetReferrer(referrer.url.spec());
139
140 int extra_load_flags = net::LOAD_NORMAL;
141 if (prefer_cache) {
142 // If there is upload data attached, only retrieve from cache because there
143 // is no current mechanism to prompt the user for their consent for a
144 // re-post. For GETs, try to retrieve data from the cache and skip
145 // validating the entry if present.
146 if (request->get_upload() != NULL)
147 extra_load_flags |= net::LOAD_ONLY_FROM_CACHE;
148 else
149 extra_load_flags |= net::LOAD_PREFERRING_CACHE;
150 } else {
151 extra_load_flags |= net::LOAD_DISABLE_CACHE;
152 }
153 request->SetLoadFlags(request->load_flags() | extra_load_flags);
154
155 // We treat a download as a main frame load, and thus update the policy URL on
156 // redirects.
157 //
158 // TODO(davidben): Is this correct? If this came from a
159 // ViewHostMsg_DownloadUrl in a frame, should it have first-party URL set
160 // appropriately?
161 request->set_first_party_url_policy(
162 net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT);
163
164 ResourceRequestInfoImpl* extra_info = new ResourceRequestInfoImpl(
asanka 2015/11/25 15:53:57 I'm still not too happy about creating this Resour
svaldez 2015/11/25 17:53:28 Wouldn't we still need to create the RRII here any
asanka 2015/11/25 19:43:11 If we are using a captive DRH, we'd need an RRII.
165 PROCESS_TYPE_RENDERER, -1, -1,
166 -1, // frame_tree_node_id
167 0, -1, -1,
168 false, // is_main_frame
169 false, // parent_is_main_frame
170 -1, // parent_render_frame_id
171 RESOURCE_TYPE_SUB_RESOURCE, ui::PAGE_TRANSITION_LINK,
172 false, // should_replace_current_entry
173 true, // is_download
174 false, // is_stream
175 true, // allow_download
176 false, // has_user_gesture
177 false, // enable_load_timing
178 false, // enable_upload_progress
179 false, // do_not_prompt_for_login
180 blink::WebReferrerPolicyDefault, blink::WebPageVisibilityStateVisible,
181 context,
182 base::WeakPtr<ResourceMessageFilter>(), // filter
183 false, // report_raw_headers
184 true, // is_async
185 false); // is_using_lofi
186
187 extra_info->set_do_not_prompt_for_login(do_not_prompt_for_login);
188 extra_info->AssociateWithRequest(request.get()); // Request takes ownership.
189
190 if (request->url().SchemeIs(url::kBlobScheme))
191 return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
192
193 extra_info->set_memory_cost(CalculateApproximateMemoryCost(request.get()));
194
195 DownloadResourceHandler* handler = new DownloadResourceHandler(
196 download_id, request.get(), started_callback, save_info.Pass());
197 handler->set_download_manager(download_manager);
198
199 // From this point forward, the |UrlDownloader| is responsible for
200 // |started_callback|.
201 linked_ptr<UrlDownloader> downloader(
202 new UrlDownloader(request.Pass(), scoped_ptr<ResourceHandler>(handler)));
203
204 downloader->Start();
205
206 g_active_downloaders.push_back(downloader);
207
208 return DOWNLOAD_INTERRUPT_REASON_NONE;
209 }
210
211 UrlDownloader::UrlDownloader(scoped_ptr<net::URLRequest> request,
212 scoped_ptr<ResourceHandler> handler)
213 : request_(request.Pass()),
214 handler_(handler.Pass()),
215 weak_ptr_factory_(this) {}
216
217 UrlDownloader::~UrlDownloader() {
218 handler_.reset();
219 }
220
221 void UrlDownloader::Start() {
222 // Give the handler a chance to delay the URLRequest from being started.
223 bool defer_start = false;
224 if (!handler_->OnWillStart(request_->url(), &defer_start)) {
225 request_->CancelWithError(net::ERR_ABORTED);
226 return;
227 }
228
229 DCHECK(!defer_start);
230 DCHECK(!request_->is_pending());
231
232 if (!request_->status().is_success()) {
233 return;
234 }
235
236 request_->set_delegate(this);
237 request_->Start();
238 }
239
240 void UrlDownloader::OnReceivedRedirect(net::URLRequest* request,
241 const net::RedirectInfo& redirect_info,
242 bool* defer_redirect) {
243 DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec();
244 request_->CancelWithError(net::ERR_ABORTED);
245 }
246
247 void UrlDownloader::OnResponseStarted(net::URLRequest* request) {
248 DVLOG(1) << "OnResponseStarted: " << request_->url().spec();
249
250 if (!request_->status().is_success()) {
251 ResponseCompleted();
252 return;
253 }
254
255 ResourceRequestInfoImpl* info =
256 ResourceRequestInfoImpl::ForRequest(request_.get());
257 scoped_refptr<ResourceResponse> response(new ResourceResponse());
258 PopulateResourceResponse(info, request_.get(), response.get());
259
260 bool defer = false;
261 if (!handler_->OnResponseStarted(response.get(), &defer)) {
262 request_->CancelWithError(net::ERR_ABORTED);
263 return;
264 }
265 DCHECK(!defer);
266
267 if (request_->status().is_success())
268 StartReading(false); // Read the first chunk.
269 else
270 ResponseCompleted();
271 }
272
273 void UrlDownloader::OnReadCompleted(net::URLRequest* request, int bytes_read) {
274 DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\""
275 << " bytes_read = " << bytes_read;
276
277 // bytes_read == -1 always implies an error.
278 if (bytes_read == -1 || !request_->status().is_success()) {
279 ResponseCompleted();
280 return;
281 }
282
283 DCHECK(bytes_read >= 0);
284 DCHECK(request_->status().is_success());
285
286 bool defer = false;
287 if (!handler_->OnReadCompleted(bytes_read, &defer)) {
288 request_->CancelWithError(net::ERR_ABORTED);
289 return;
290 }
291 DCHECK(!defer);
292
293 if (!request_->status().is_success())
294 return;
295
296 if (bytes_read > 0) {
297 StartReading(true); // Read the next chunk.
298 } else {
299 // URLRequest reported an EOF. Call ResponseCompleted.
300 DCHECK_EQ(0, bytes_read);
301 ResponseCompleted();
302 }
303 }
304
305 void UrlDownloader::StartReading(bool is_continuation) {
306 int bytes_read;
307
308 // Make sure we track the buffer in at least one place. This ensures it gets
309 // deleted even in the case the request has already finished its job and
310 // doesn't use the buffer.
311 scoped_refptr<net::IOBuffer> buf;
312 int buf_size;
313 if (!handler_->OnWillRead(&buf, &buf_size, -1)) {
314 request_->CancelWithError(net::ERR_ABORTED);
315 return;
316 }
317
318 DCHECK(buf.get());
319 DCHECK(buf_size > 0);
320
321 request_->Read(buf.get(), buf_size, &bytes_read);
322
323 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
324 if (request_->status().is_io_pending())
325 return;
326
327 if (!is_continuation || bytes_read <= 0) {
328 OnReadCompleted(request_.get(), bytes_read);
329 } else {
330 // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
331 // thread in case the URLRequest can provide data synchronously.
332 base::ThreadTaskRunnerHandle::Get()->PostTask(
333 FROM_HERE,
334 base::Bind(&UrlDownloader::OnReadCompleted,
335 weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
336 }
337 }
338
339 void UrlDownloader::ResponseCompleted() {
340 DVLOG(1) << "ResponseCompleted: " << request_->url().spec();
341
342 ResourceRequestInfoImpl* info =
343 ResourceRequestInfoImpl::ForRequest(request_.get());
344
345 std::string security_info;
346 const net::SSLInfo& ssl_info = request_->ssl_info();
347 if (ssl_info.cert.get() != NULL) {
348 SSLStatus ssl_status;
349 GetSSLStatusForRequest(request_->url(), ssl_info, info->GetChildID(),
350 &ssl_status);
351
352 security_info = SerializeSecurityInfo(ssl_status);
353 }
354
355 bool defer = false;
356 handler_->OnResponseCompleted(request_->status(), security_info, &defer);
357 DCHECK(!defer);
358 }
359
360 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698