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

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

Issue 10074001: Initial implementation of the ByteStream refactor. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 months 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 | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698