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

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: Checkpoint and merge to LKGR. Created 8 years, 7 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 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
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
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
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
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 }
OLDNEW
« no previous file with comments | « content/browser/download/download_resource_handler.h ('k') | content/browser/download/download_stats.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698