OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/download/download_resource_handler.h" | 5 #include "content/browser/download/download_resource_handler.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/message_loop_proxy.h" | |
11 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
12 #include "base/metrics/stats_counters.h" | 13 #include "base/metrics/stats_counters.h" |
13 #include "base/stringprintf.h" | 14 #include "base/stringprintf.h" |
14 #include "content/browser/download/download_buffer.h" | |
15 #include "content/browser/download/download_create_info.h" | 15 #include "content/browser/download/download_create_info.h" |
16 #include "content/browser/download/download_file_manager.h" | 16 #include "content/browser/download/download_file_manager.h" |
17 #include "content/browser/download/download_interrupt_reasons_impl.h" | 17 #include "content/browser/download/download_interrupt_reasons_impl.h" |
18 #include "content/browser/download/download_manager_impl.h" | 18 #include "content/browser/download/download_manager_impl.h" |
19 #include "content/browser/download/download_request_handle.h" | 19 #include "content/browser/download/download_request_handle.h" |
20 #include "content/browser/download/byte_stream.h" | |
20 #include "content/browser/download/download_stats.h" | 21 #include "content/browser/download/download_stats.h" |
21 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" | 22 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" |
22 #include "content/browser/renderer_host/resource_request_info_impl.h" | 23 #include "content/browser/renderer_host/resource_request_info_impl.h" |
23 #include "content/public/browser/browser_thread.h" | 24 #include "content/public/browser/browser_thread.h" |
24 #include "content/public/browser/download_interrupt_reasons.h" | 25 #include "content/public/browser/download_interrupt_reasons.h" |
25 #include "content/public/browser/download_item.h" | 26 #include "content/public/browser/download_item.h" |
26 #include "content/public/browser/download_manager_delegate.h" | 27 #include "content/public/browser/download_manager_delegate.h" |
27 #include "content/public/common/resource_response.h" | 28 #include "content/public/common/resource_response.h" |
28 #include "net/base/io_buffer.h" | 29 #include "net/base/io_buffer.h" |
29 #include "net/base/net_errors.h" | 30 #include "net/base/net_errors.h" |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
61 const DownloadResourceHandler::OnStartedCallback& started_cb, | 62 const DownloadResourceHandler::OnStartedCallback& started_cb, |
62 const content::DownloadSaveInfo& save_info) | 63 const content::DownloadSaveInfo& save_info) |
63 : download_id_(DownloadId::Invalid()), | 64 : download_id_(DownloadId::Invalid()), |
64 global_id_(render_process_host_id, request_id), | 65 global_id_(render_process_host_id, request_id), |
65 render_view_id_(render_view_id), | 66 render_view_id_(render_view_id), |
66 content_length_(0), | 67 content_length_(0), |
67 download_file_manager_(download_file_manager), | 68 download_file_manager_(download_file_manager), |
68 request_(request), | 69 request_(request), |
69 started_cb_(started_cb), | 70 started_cb_(started_cb), |
70 save_info_(save_info), | 71 save_info_(save_info), |
71 buffer_(new content::DownloadBuffer), | |
72 is_paused_(false), | 72 is_paused_(false), |
73 last_buffer_size_(0), | 73 last_buffer_size_(0), |
74 bytes_read_(0) { | 74 bytes_read_(0), |
75 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
75 download_stats::RecordDownloadCount(download_stats::UNTHROTTLED_COUNT); | 76 download_stats::RecordDownloadCount(download_stats::UNTHROTTLED_COUNT); |
76 } | 77 } |
77 | 78 |
78 bool DownloadResourceHandler::OnUploadProgress(int request_id, | 79 bool DownloadResourceHandler::OnUploadProgress(int request_id, |
79 uint64 position, | 80 uint64 position, |
80 uint64 size) { | 81 uint64 size) { |
81 return true; | 82 return true; |
82 } | 83 } |
83 | 84 |
84 // Not needed, as this event handler ought to be the final resource. | 85 // Not needed, as this event handler ought to be the final resource. |
(...skipping 23 matching lines...) Expand all Loading... | |
108 | 109 |
109 std::string content_disposition; | 110 std::string content_disposition; |
110 request_->GetResponseHeaderByName("content-disposition", | 111 request_->GetResponseHeaderByName("content-disposition", |
111 &content_disposition); | 112 &content_disposition); |
112 set_content_disposition(content_disposition); | 113 set_content_disposition(content_disposition); |
113 set_content_length(response->content_length); | 114 set_content_length(response->content_length); |
114 | 115 |
115 const ResourceRequestInfoImpl* request_info = | 116 const ResourceRequestInfoImpl* request_info = |
116 ResourceRequestInfoImpl::ForRequest(request_); | 117 ResourceRequestInfoImpl::ForRequest(request_); |
117 | 118 |
119 // Create the ByteStream pipe for sending data to the download sink. | |
120 output_pipe_ = new content::ByteStream(); | |
121 output_pipe_->RegisterSourceCallback( | |
122 base::MessageLoopProxy::current(), | |
123 base::Bind(&DownloadResourceHandler::ThrottleRequest, | |
124 weak_factory_.GetWeakPtr())); | |
125 | |
118 // Deleted in DownloadManager. | 126 // Deleted in DownloadManager. |
119 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( | 127 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( |
120 base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, | 128 base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, |
121 request_->net_log(), request_info->has_user_gesture(), | 129 request_->net_log(), request_info->has_user_gesture(), |
122 request_info->transition_type())); | 130 request_info->transition_type())); |
123 info->url_chain = request_->url_chain(); | 131 info->url_chain = request_->url_chain(); |
124 info->referrer_url = GURL(request_->referrer()); | 132 info->referrer_url = GURL(request_->referrer()); |
125 info->start_time = base::Time::Now(); | 133 info->start_time = base::Time::Now(); |
126 info->received_bytes = save_info_.offset; | 134 info->received_bytes = save_info_.offset; |
127 info->total_bytes = content_length_; | 135 info->total_bytes = content_length_; |
(...skipping 28 matching lines...) Expand all Loading... | |
156 !response->headers->EnumerateHeader(NULL, | 164 !response->headers->EnumerateHeader(NULL, |
157 "Accept-Ranges", | 165 "Accept-Ranges", |
158 &accept_ranges_)) { | 166 &accept_ranges_)) { |
159 accept_ranges_ = ""; | 167 accept_ranges_ = ""; |
160 } | 168 } |
161 | 169 |
162 info->prompt_user_for_save_location = | 170 info->prompt_user_for_save_location = |
163 save_info_.prompt_for_save_location && save_info_.file_path.empty(); | 171 save_info_.prompt_for_save_location && save_info_.file_path.empty(); |
164 info->referrer_charset = request_->context()->referrer_charset(); | 172 info->referrer_charset = request_->context()->referrer_charset(); |
165 info->save_info = save_info_; | 173 info->save_info = save_info_; |
166 | 174 info->pipe = output_pipe_; |
167 | 175 |
168 BrowserThread::PostTask( | 176 BrowserThread::PostTask( |
169 BrowserThread::UI, FROM_HERE, | 177 BrowserThread::UI, FROM_HERE, |
170 base::Bind(&DownloadResourceHandler::StartOnUIThread, this, | 178 base::Bind(&DownloadResourceHandler::StartOnUIThread, this, |
171 base::Passed(&info), request_handle)); | 179 base::Passed(&info), request_handle)); |
172 | 180 |
173 // We can't start saving the data before we create the file on disk and have a | |
ahendrickson
2012/04/16 15:14:27
Nice!
Randy Smith (Not in Mondays)
2012/04/18 19:10:38
:-).
| |
174 // download id. The request will be un-paused in | |
175 // DownloadFileManager::CreateDownloadFile. | |
176 ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, | |
177 global_id_.request_id, | |
178 true); | |
179 | |
180 return true; | 181 return true; |
181 } | 182 } |
182 | 183 |
183 void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { | 184 void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { |
184 if (started_cb_.is_null()) | 185 if (started_cb_.is_null()) |
185 return; | 186 return; |
186 BrowserThread::PostTask( | 187 BrowserThread::PostTask( |
187 BrowserThread::UI, FROM_HERE, | 188 BrowserThread::UI, FROM_HERE, |
188 base::Bind(&CallStartedCBOnUIThread, started_cb_, id, error)); | 189 base::Bind(&CallStartedCBOnUIThread, started_cb_, id, error)); |
189 started_cb_.Reset(); | 190 started_cb_.Reset(); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
222 double actual_bandwidth = (*bytes_read)/seconds_since_last_read; | 223 double actual_bandwidth = (*bytes_read)/seconds_since_last_read; |
223 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; | 224 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; |
224 download_stats::RecordBandwidth(actual_bandwidth, potential_bandwidth); | 225 download_stats::RecordBandwidth(actual_bandwidth, potential_bandwidth); |
225 } | 226 } |
226 last_read_time_ = now; | 227 last_read_time_ = now; |
227 | 228 |
228 if (!*bytes_read) | 229 if (!*bytes_read) |
229 return true; | 230 return true; |
230 bytes_read_ += *bytes_read; | 231 bytes_read_ += *bytes_read; |
231 DCHECK(read_buffer_); | 232 DCHECK(read_buffer_); |
232 // Swap the data. | |
233 net::IOBuffer* io_buffer = NULL; | |
234 read_buffer_.swap(&io_buffer); | |
235 size_t vector_size = buffer_->AddData(io_buffer, *bytes_read); | |
236 bool need_update = (vector_size == 1); // Buffer was empty. | |
237 | 233 |
238 // We are passing ownership of this buffer to the download file manager. | 234 // Take the data from the DataLoanIOBuffer and ship it down the pipe. |
239 if (need_update) { | 235 // If the pipe is full, pause the request; the pipe callback will resume it. |
236 if (!output_pipe_->AddData(read_buffer_, *bytes_read)) { | |
237 // Send a message to ourselves to pause the request. | |
238 // Posting to get around fragile/broken rentrancy semantics in | |
239 // ResourceDispatcherHost. | |
240 // TODO(rdsmith): Remove PostTask when RDH is fixed. | |
240 BrowserThread::PostTask( | 241 BrowserThread::PostTask( |
241 BrowserThread::FILE, FROM_HERE, | 242 BrowserThread::IO, FROM_HERE, |
242 base::Bind(&DownloadFileManager::UpdateDownload, | 243 base::Bind( |
243 download_file_manager_, download_id_, buffer_)); | 244 &DownloadResourceHandler::ThrottleRequest, |
245 weak_factory_.GetWeakPtr())); | |
244 } | 246 } |
245 | 247 |
246 // We schedule a pause outside of the read loop if there is too much file | 248 read_buffer_ = NULL; // Drop our reference. |
247 // writing work to do. | |
248 if (vector_size > kLoadsToWrite) | |
249 StartPauseTimer(); | |
250 | 249 |
251 return true; | 250 return true; |
252 } | 251 } |
253 | 252 |
254 bool DownloadResourceHandler::OnResponseCompleted( | 253 bool DownloadResourceHandler::OnResponseCompleted( |
255 int request_id, | 254 int request_id, |
256 const net::URLRequestStatus& status, | 255 const net::URLRequestStatus& status, |
257 const std::string& security_info) { | 256 const std::string& security_info) { |
258 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 257 VLOG(20) << __FUNCTION__ << "()" << DebugString() |
259 << " request_id = " << request_id | 258 << " request_id = " << request_id |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
324 break; | 323 break; |
325 } | 324 } |
326 } | 325 } |
327 } | 326 } |
328 | 327 |
329 download_stats::RecordAcceptsRanges(accept_ranges_, bytes_read_); | 328 download_stats::RecordAcceptsRanges(accept_ranges_, bytes_read_); |
330 | 329 |
331 // If the callback was already run on the UI thread, this will be a noop. | 330 // If the callback was already run on the UI thread, this will be a noop. |
332 CallStartedCB(download_id_, error_code); | 331 CallStartedCB(download_id_, error_code); |
333 | 332 |
334 // We transfer ownership to |DownloadFileManager| to delete |buffer_|, | 333 // Send the info down the pipe. |
335 // so that any functions queued up on the FILE thread are executed | 334 if (output_pipe_.get()) |
336 // before deletion. | 335 output_pipe_->SourceComplete(reason); |
337 BrowserThread::PostTask( | 336 |
338 BrowserThread::FILE, FROM_HERE, | 337 output_pipe_ = NULL; // We no longer need the pipe. |
339 base::Bind(&DownloadFileManager::OnResponseCompleted, | |
340 download_file_manager_, download_id_, reason, security_info)); | |
341 buffer_ = NULL; // The buffer is longer needed by |DownloadResourceHandler|. | |
342 read_buffer_ = NULL; | 338 read_buffer_ = NULL; |
343 } | 339 } |
344 | 340 |
345 void DownloadResourceHandler::OnRequestClosed() { | 341 void DownloadResourceHandler::OnRequestClosed() { |
346 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", | 342 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", |
347 base::TimeTicks::Now() - download_start_time_); | 343 base::TimeTicks::Now() - download_start_time_); |
348 } | 344 } |
349 | 345 |
350 void DownloadResourceHandler::StartOnUIThread( | 346 void DownloadResourceHandler::StartOnUIThread( |
351 scoped_ptr<DownloadCreateInfo> info, | 347 scoped_ptr<DownloadCreateInfo> info, |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
383 content_length_ = 0; | 379 content_length_ = 0; |
384 if (content_length > 0) | 380 if (content_length > 0) |
385 content_length_ = content_length; | 381 content_length_ = content_length; |
386 } | 382 } |
387 | 383 |
388 void DownloadResourceHandler::set_content_disposition( | 384 void DownloadResourceHandler::set_content_disposition( |
389 const std::string& content_disposition) { | 385 const std::string& content_disposition) { |
390 content_disposition_ = content_disposition; | 386 content_disposition_ = content_disposition; |
391 } | 387 } |
392 | 388 |
393 void DownloadResourceHandler::CheckWriteProgress() { | |
394 if (!buffer_.get()) | |
395 return; // The download completed while we were waiting to run. | |
396 | |
397 size_t contents_size = buffer_->size(); | |
398 | |
399 bool should_pause = contents_size > kLoadsToWrite; | |
400 | |
401 // We'll come back later and see if it's okay to unpause the request. | |
402 if (should_pause) | |
403 StartPauseTimer(); | |
404 | |
405 if (is_paused_ != should_pause) { | |
406 ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, | |
407 global_id_.request_id, | |
408 should_pause); | |
409 is_paused_ = should_pause; | |
410 } | |
411 } | |
412 | |
413 DownloadResourceHandler::~DownloadResourceHandler() { | 389 DownloadResourceHandler::~DownloadResourceHandler() { |
414 // This won't do anything if the callback was called before. | 390 // This won't do anything if the callback was called before. |
415 // If it goes through, it will likely be because OnWillStart() returned | 391 // If it goes through, it will likely be because OnWillStart() returned |
416 // false somewhere in the chain of resource handlers. | 392 // false somewhere in the chain of resource handlers. |
417 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); | 393 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); |
394 | |
395 // Remove output pipe callback if a pipe exists. | |
396 if (output_pipe_.get()) | |
397 output_pipe_->RegisterSourceCallback(scoped_refptr<base::TaskRunner>(), | |
398 base::Closure()); | |
418 } | 399 } |
419 | 400 |
420 void DownloadResourceHandler::StartPauseTimer() { | 401 // Note subtlety here!! We need to post a task to ourselves to throttle the |
421 if (!pause_timer_.IsRunning()) | 402 // request because ResourceDispatcherHost isn't reentrant around pausing |
422 pause_timer_.Start(FROM_HERE, | 403 // requests from ResourceHandlers. But that opens up a window during which |
423 base::TimeDelta::FromMilliseconds(kThrottleTimeMs), this, | 404 // notification of having emptied the pipe might arrive from the destination, |
424 &DownloadResourceHandler::CheckWriteProgress); | 405 // and be dropped because we weren't paused at that point. So whenever we |
406 // get a throttle request, we figure out for ourselves if we should be | |
407 // paused or not, and adjust state appropriately. This will only fail if | |
408 // we don't get a notification of state transition, and that shouldn't | |
409 // happen; pipe full events are detected in OnReadCompleted and posted here, | |
410 // and pipe unfull events are detected on destination read and posted here. | |
411 void DownloadResourceHandler::ThrottleRequest() { | |
412 bool pause_goal = output_pipe_->IsFull(); | |
413 | |
414 if (pause_goal == is_paused_) | |
415 // No need to do anything. | |
416 return; | |
417 | |
418 ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, | |
419 global_id_.request_id, | |
420 pause_goal); | |
421 is_paused_ = pause_goal; | |
425 } | 422 } |
426 | 423 |
424 | |
427 std::string DownloadResourceHandler::DebugString() const { | 425 std::string DownloadResourceHandler::DebugString() const { |
428 return base::StringPrintf("{" | 426 return base::StringPrintf("{" |
429 " url_ = " "\"%s\"" | 427 " url_ = " "\"%s\"" |
430 " download_id_ = " "%d" | 428 " download_id_ = " "%d" |
431 " global_id_ = {" | 429 " global_id_ = {" |
432 " child_id = " "%d" | 430 " child_id = " "%d" |
433 " request_id = " "%d" | 431 " request_id = " "%d" |
434 " }" | 432 " }" |
435 " render_view_id_ = " "%d" | 433 " render_view_id_ = " "%d" |
436 " save_info_.file_path = \"%" PRFilePath "\"" | 434 " save_info_.file_path = \"%" PRFilePath "\"" |
437 " }", | 435 " }", |
438 request_ ? | 436 request_ ? |
439 request_->url().spec().c_str() : | 437 request_->url().spec().c_str() : |
440 "<NULL request>", | 438 "<NULL request>", |
441 download_id_.local(), | 439 download_id_.local(), |
442 global_id_.child_id, | 440 global_id_.child_id, |
443 global_id_.request_id, | 441 global_id_.request_id, |
444 render_view_id_, | 442 render_view_id_, |
445 save_info_.file_path.value().c_str()); | 443 save_info_.file_path.value().c_str()); |
446 } | 444 } |
OLD | NEW |