| 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 33); |
| 126 |
| 118 // Deleted in DownloadManager. | 127 // Deleted in DownloadManager. |
| 119 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( | 128 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( |
| 120 base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, | 129 base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, |
| 121 request_->net_log(), request_info->has_user_gesture(), | 130 request_->net_log(), request_info->has_user_gesture(), |
| 122 request_info->transition_type())); | 131 request_info->transition_type())); |
| 123 info->url_chain = request_->url_chain(); | 132 info->url_chain = request_->url_chain(); |
| 124 info->referrer_url = GURL(request_->referrer()); | 133 info->referrer_url = GURL(request_->referrer()); |
| 125 info->start_time = base::Time::Now(); | 134 info->start_time = base::Time::Now(); |
| 126 info->received_bytes = save_info_.offset; | 135 info->received_bytes = save_info_.offset; |
| 127 info->total_bytes = content_length_; | 136 info->total_bytes = content_length_; |
| (...skipping 28 matching lines...) Expand all Loading... |
| 156 !response->headers->EnumerateHeader(NULL, | 165 !response->headers->EnumerateHeader(NULL, |
| 157 "Accept-Ranges", | 166 "Accept-Ranges", |
| 158 &accept_ranges_)) { | 167 &accept_ranges_)) { |
| 159 accept_ranges_ = ""; | 168 accept_ranges_ = ""; |
| 160 } | 169 } |
| 161 | 170 |
| 162 info->prompt_user_for_save_location = | 171 info->prompt_user_for_save_location = |
| 163 save_info_.prompt_for_save_location && save_info_.file_path.empty(); | 172 save_info_.prompt_for_save_location && save_info_.file_path.empty(); |
| 164 info->referrer_charset = request_->context()->referrer_charset(); | 173 info->referrer_charset = request_->context()->referrer_charset(); |
| 165 info->save_info = save_info_; | 174 info->save_info = save_info_; |
| 166 | 175 info->pipe = output_pipe_; |
| 167 | 176 |
| 168 BrowserThread::PostTask( | 177 BrowserThread::PostTask( |
| 169 BrowserThread::UI, FROM_HERE, | 178 BrowserThread::UI, FROM_HERE, |
| 170 base::Bind(&DownloadResourceHandler::StartOnUIThread, this, | 179 base::Bind(&DownloadResourceHandler::StartOnUIThread, this, |
| 171 base::Passed(&info), request_handle)); | 180 base::Passed(&info), request_handle)); |
| 172 | 181 |
| 173 // We can't start saving the data before we create the file on disk and have a | |
| 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; | 182 return true; |
| 181 } | 183 } |
| 182 | 184 |
| 183 void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { | 185 void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { |
| 184 if (started_cb_.is_null()) | 186 if (started_cb_.is_null()) |
| 185 return; | 187 return; |
| 186 BrowserThread::PostTask( | 188 BrowserThread::PostTask( |
| 187 BrowserThread::UI, FROM_HERE, | 189 BrowserThread::UI, FROM_HERE, |
| 188 base::Bind(&CallStartedCBOnUIThread, started_cb_, id, error)); | 190 base::Bind(&CallStartedCBOnUIThread, started_cb_, id, error)); |
| 189 started_cb_.Reset(); | 191 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; | 224 double actual_bandwidth = (*bytes_read)/seconds_since_last_read; |
| 223 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; | 225 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; |
| 224 download_stats::RecordBandwidth(actual_bandwidth, potential_bandwidth); | 226 download_stats::RecordBandwidth(actual_bandwidth, potential_bandwidth); |
| 225 } | 227 } |
| 226 last_read_time_ = now; | 228 last_read_time_ = now; |
| 227 | 229 |
| 228 if (!*bytes_read) | 230 if (!*bytes_read) |
| 229 return true; | 231 return true; |
| 230 bytes_read_ += *bytes_read; | 232 bytes_read_ += *bytes_read; |
| 231 DCHECK(read_buffer_); | 233 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 | 234 |
| 238 // We are passing ownership of this buffer to the download file manager. | 235 // Take the data from the DataLoanIOBuffer and ship it down the pipe. |
| 239 if (need_update) { | 236 // If the pipe is full, pause the request; the pipe callback will resume it. |
| 237 if (!output_pipe_->AddData(read_buffer_, *bytes_read)) { |
| 238 // Send a message to ourselves to pause the request. |
| 239 // Posting to get around fragile/broken rentrancy semantics in |
| 240 // ResourceDispatcherHost. |
| 241 // TODO(rdsmith): Remove PostTask when RDH is fixed. |
| 240 BrowserThread::PostTask( | 242 BrowserThread::PostTask( |
| 241 BrowserThread::FILE, FROM_HERE, | 243 BrowserThread::IO, FROM_HERE, |
| 242 base::Bind(&DownloadFileManager::UpdateDownload, | 244 base::Bind( |
| 243 download_file_manager_, download_id_, buffer_)); | 245 &DownloadResourceHandler::ThrottleRequest, |
| 246 weak_factory_.GetWeakPtr())); |
| 244 } | 247 } |
| 245 | 248 |
| 246 // We schedule a pause outside of the read loop if there is too much file | 249 read_buffer_ = NULL; // Drop our reference. |
| 247 // writing work to do. | |
| 248 if (vector_size > kLoadsToWrite) | |
| 249 StartPauseTimer(); | |
| 250 | 250 |
| 251 return true; | 251 return true; |
| 252 } | 252 } |
| 253 | 253 |
| 254 bool DownloadResourceHandler::OnResponseCompleted( | 254 bool DownloadResourceHandler::OnResponseCompleted( |
| 255 int request_id, | 255 int request_id, |
| 256 const net::URLRequestStatus& status, | 256 const net::URLRequestStatus& status, |
| 257 const std::string& security_info) { | 257 const std::string& security_info) { |
| 258 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 258 VLOG(20) << __FUNCTION__ << "()" << DebugString() |
| 259 << " request_id = " << request_id | 259 << " request_id = " << request_id |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 325 break; | 325 break; |
| 326 } | 326 } |
| 327 } | 327 } |
| 328 } | 328 } |
| 329 | 329 |
| 330 download_stats::RecordAcceptsRanges(accept_ranges_, bytes_read_); | 330 download_stats::RecordAcceptsRanges(accept_ranges_, bytes_read_); |
| 331 | 331 |
| 332 // If the callback was already run on the UI thread, this will be a noop. | 332 // If the callback was already run on the UI thread, this will be a noop. |
| 333 CallStartedCB(download_id_, error_code); | 333 CallStartedCB(download_id_, error_code); |
| 334 | 334 |
| 335 // We transfer ownership to |DownloadFileManager| to delete |buffer_|, | 335 // Send the info down the pipe. |
| 336 // so that any functions queued up on the FILE thread are executed | 336 if (output_pipe_.get()) |
| 337 // before deletion. | 337 output_pipe_->SourceComplete(reason); |
| 338 BrowserThread::PostTask( | 338 |
| 339 BrowserThread::FILE, FROM_HERE, | 339 output_pipe_ = NULL; // We no longer need the pipe. |
| 340 base::Bind(&DownloadFileManager::OnResponseCompleted, | |
| 341 download_file_manager_, download_id_, reason, security_info)); | |
| 342 buffer_ = NULL; // The buffer is longer needed by |DownloadResourceHandler|. | |
| 343 read_buffer_ = NULL; | 340 read_buffer_ = NULL; |
| 344 } | 341 } |
| 345 | 342 |
| 346 void DownloadResourceHandler::OnRequestClosed() { | 343 void DownloadResourceHandler::OnRequestClosed() { |
| 347 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", | 344 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", |
| 348 base::TimeTicks::Now() - download_start_time_); | 345 base::TimeTicks::Now() - download_start_time_); |
| 349 } | 346 } |
| 350 | 347 |
| 351 void DownloadResourceHandler::StartOnUIThread( | 348 void DownloadResourceHandler::StartOnUIThread( |
| 352 scoped_ptr<DownloadCreateInfo> info, | 349 scoped_ptr<DownloadCreateInfo> info, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 384 content_length_ = 0; | 381 content_length_ = 0; |
| 385 if (content_length > 0) | 382 if (content_length > 0) |
| 386 content_length_ = content_length; | 383 content_length_ = content_length; |
| 387 } | 384 } |
| 388 | 385 |
| 389 void DownloadResourceHandler::set_content_disposition( | 386 void DownloadResourceHandler::set_content_disposition( |
| 390 const std::string& content_disposition) { | 387 const std::string& content_disposition) { |
| 391 content_disposition_ = content_disposition; | 388 content_disposition_ = content_disposition; |
| 392 } | 389 } |
| 393 | 390 |
| 394 void DownloadResourceHandler::CheckWriteProgress() { | |
| 395 if (!buffer_.get()) | |
| 396 return; // The download completed while we were waiting to run. | |
| 397 | |
| 398 size_t contents_size = buffer_->size(); | |
| 399 | |
| 400 bool should_pause = contents_size > kLoadsToWrite; | |
| 401 | |
| 402 // We'll come back later and see if it's okay to unpause the request. | |
| 403 if (should_pause) | |
| 404 StartPauseTimer(); | |
| 405 | |
| 406 if (is_paused_ != should_pause) { | |
| 407 ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, | |
| 408 global_id_.request_id, | |
| 409 should_pause); | |
| 410 is_paused_ = should_pause; | |
| 411 } | |
| 412 } | |
| 413 | |
| 414 DownloadResourceHandler::~DownloadResourceHandler() { | 391 DownloadResourceHandler::~DownloadResourceHandler() { |
| 415 // This won't do anything if the callback was called before. | 392 // This won't do anything if the callback was called before. |
| 416 // If it goes through, it will likely be because OnWillStart() returned | 393 // If it goes through, it will likely be because OnWillStart() returned |
| 417 // false somewhere in the chain of resource handlers. | 394 // false somewhere in the chain of resource handlers. |
| 418 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); | 395 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); |
| 396 |
| 397 // Remove output pipe callback if a pipe exists. |
| 398 if (output_pipe_.get()) |
| 399 output_pipe_->RegisterSourceCallback(scoped_refptr<base::TaskRunner>(), |
| 400 base::Closure(), 0); |
| 419 } | 401 } |
| 420 | 402 |
| 421 void DownloadResourceHandler::StartPauseTimer() { | 403 // Note subtlety here!! We need to post a task to ourselves to throttle the |
| 422 if (!pause_timer_.IsRunning()) | 404 // request because ResourceDispatcherHost isn't reentrant around pausing |
| 423 pause_timer_.Start(FROM_HERE, | 405 // requests from ResourceHandlers. But that opens up a window during which |
| 424 base::TimeDelta::FromMilliseconds(kThrottleTimeMs), this, | 406 // notification of having emptied the pipe might arrive from the destination, |
| 425 &DownloadResourceHandler::CheckWriteProgress); | 407 // and be dropped because we weren't paused at that point. So whenever we |
| 408 // get a throttle request, we figure out for ourselves if we should be |
| 409 // paused or not, and adjust state appropriately. This will only fail if |
| 410 // we don't get a notification of state transition, and that shouldn't |
| 411 // happen; pipe full events are detected in OnReadCompleted and posted here, |
| 412 // and pipe unfull events are detected on destination read and posted here. |
| 413 void DownloadResourceHandler::ThrottleRequest() { |
| 414 bool pause_goal = output_pipe_->IsFull(); |
| 415 |
| 416 if (pause_goal == is_paused_) |
| 417 // No need to do anything. |
| 418 return; |
| 419 |
| 420 ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, |
| 421 global_id_.request_id, |
| 422 pause_goal); |
| 423 is_paused_ = pause_goal; |
| 426 } | 424 } |
| 427 | 425 |
| 426 |
| 428 std::string DownloadResourceHandler::DebugString() const { | 427 std::string DownloadResourceHandler::DebugString() const { |
| 429 return base::StringPrintf("{" | 428 return base::StringPrintf("{" |
| 430 " url_ = " "\"%s\"" | 429 " url_ = " "\"%s\"" |
| 431 " download_id_ = " "%d" | 430 " download_id_ = " "%d" |
| 432 " global_id_ = {" | 431 " global_id_ = {" |
| 433 " child_id = " "%d" | 432 " child_id = " "%d" |
| 434 " request_id = " "%d" | 433 " request_id = " "%d" |
| 435 " }" | 434 " }" |
| 436 " render_view_id_ = " "%d" | 435 " render_view_id_ = " "%d" |
| 437 " save_info_.file_path = \"%" PRFilePath "\"" | 436 " save_info_.file_path = \"%" PRFilePath "\"" |
| 438 " }", | 437 " }", |
| 439 request_ ? | 438 request_ ? |
| 440 request_->url().spec().c_str() : | 439 request_->url().spec().c_str() : |
| 441 "<NULL request>", | 440 "<NULL request>", |
| 442 download_id_.local(), | 441 download_id_.local(), |
| 443 global_id_.child_id, | 442 global_id_.child_id, |
| 444 global_id_.request_id, | 443 global_id_.request_id, |
| 445 render_view_id_, | 444 render_view_id_, |
| 446 save_info_.file_path.value().c_str()); | 445 save_info_.file_path.value().c_str()); |
| 447 } | 446 } |
| OLD | NEW |