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" |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
62 const content::DownloadSaveInfo& save_info) | 62 const content::DownloadSaveInfo& save_info) |
63 : download_id_(DownloadId::Invalid()), | 63 : download_id_(DownloadId::Invalid()), |
64 global_id_(render_process_host_id, request_id), | 64 global_id_(render_process_host_id, request_id), |
65 render_view_id_(render_view_id), | 65 render_view_id_(render_view_id), |
66 content_length_(0), | 66 content_length_(0), |
67 download_file_manager_(download_file_manager), | 67 download_file_manager_(download_file_manager), |
68 request_(request), | 68 request_(request), |
69 started_cb_(started_cb), | 69 started_cb_(started_cb), |
70 save_info_(save_info), | 70 save_info_(save_info), |
71 buffer_(new content::DownloadBuffer), | 71 buffer_(new content::DownloadBuffer), |
72 is_paused_(false), | |
73 last_buffer_size_(0), | 72 last_buffer_size_(0), |
74 bytes_read_(0) { | 73 bytes_read_(0), |
| 74 pause_count_(0), |
| 75 was_deferred_(false) { |
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. |
85 bool DownloadResourceHandler::OnRequestRedirected( | 86 bool DownloadResourceHandler::OnRequestRedirected( |
86 int request_id, | 87 int request_id, |
87 const GURL& url, | 88 const GURL& url, |
88 content::ResourceResponse* response, | 89 content::ResourceResponse* response, |
89 bool* defer) { | 90 bool* defer) { |
90 return true; | 91 return true; |
91 } | 92 } |
92 | 93 |
93 // Send the download creation information to the download thread. | 94 // Send the download creation information to the download thread. |
94 bool DownloadResourceHandler::OnResponseStarted( | 95 bool DownloadResourceHandler::OnResponseStarted( |
95 int request_id, | 96 int request_id, |
96 content::ResourceResponse* response) { | 97 content::ResourceResponse* response, |
| 98 bool* defer) { |
97 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 99 VLOG(20) << __FUNCTION__ << "()" << DebugString() |
98 << " request_id = " << request_id; | 100 << " request_id = " << request_id; |
99 download_start_time_ = base::TimeTicks::Now(); | 101 download_start_time_ = base::TimeTicks::Now(); |
100 | 102 |
101 // If it's a download, we don't want to poison the cache with it. | 103 // If it's a download, we don't want to poison the cache with it. |
102 request_->StopCaching(); | 104 request_->StopCaching(); |
103 | 105 |
104 std::string content_disposition; | 106 std::string content_disposition; |
105 request_->GetResponseHeaderByName("content-disposition", | 107 request_->GetResponseHeaderByName("content-disposition", |
106 &content_disposition); | 108 &content_disposition); |
107 set_content_disposition(content_disposition); | 109 SetContentDisposition(content_disposition); |
108 set_content_length(response->content_length); | 110 SetContentLength(response->content_length); |
109 | 111 |
110 const ResourceRequestInfoImpl* request_info = | 112 const ResourceRequestInfoImpl* request_info = |
111 ResourceRequestInfoImpl::ForRequest(request_); | 113 ResourceRequestInfoImpl::ForRequest(request_); |
112 | 114 |
113 // Deleted in DownloadManager. | 115 // Deleted in DownloadManager. |
114 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( | 116 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( |
115 base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, | 117 base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, |
116 request_->net_log(), request_info->has_user_gesture(), | 118 request_->net_log(), request_info->has_user_gesture(), |
117 request_info->transition_type())); | 119 request_info->transition_type())); |
118 info->url_chain = request_->url_chain(); | 120 info->url_chain = request_->url_chain(); |
119 info->referrer_url = GURL(request_->referrer()); | 121 info->referrer_url = GURL(request_->referrer()); |
120 info->start_time = base::Time::Now(); | 122 info->start_time = base::Time::Now(); |
121 info->received_bytes = save_info_.offset; | 123 info->received_bytes = save_info_.offset; |
122 info->total_bytes = content_length_; | 124 info->total_bytes = content_length_; |
123 info->state = DownloadItem::IN_PROGRESS; | 125 info->state = DownloadItem::IN_PROGRESS; |
124 info->has_user_gesture = request_info->has_user_gesture(); | 126 info->has_user_gesture = request_info->has_user_gesture(); |
125 info->content_disposition = content_disposition_; | 127 info->content_disposition = content_disposition_; |
126 info->mime_type = response->mime_type; | 128 info->mime_type = response->mime_type; |
127 info->remote_address = request_->GetSocketAddress().host(); | 129 info->remote_address = request_->GetSocketAddress().host(); |
128 download_stats::RecordDownloadMimeType(info->mime_type); | 130 download_stats::RecordDownloadMimeType(info->mime_type); |
129 | 131 |
130 DownloadRequestHandle request_handle(global_id_.child_id, | 132 DownloadRequestHandle request_handle(this, global_id_.child_id, |
131 render_view_id_, global_id_.request_id); | 133 render_view_id_, global_id_.request_id); |
132 | 134 |
133 // Get the last modified time and etag. | 135 // Get the last modified time and etag. |
134 const net::HttpResponseHeaders* headers = request_->response_headers(); | 136 const net::HttpResponseHeaders* headers = request_->response_headers(); |
135 if (headers) { | 137 if (headers) { |
136 std::string last_modified_hdr; | 138 std::string last_modified_hdr; |
137 std::string etag; | 139 std::string etag; |
138 if (headers->EnumerateHeader(NULL, "Last-Modified", &last_modified_hdr)) | 140 if (headers->EnumerateHeader(NULL, "Last-Modified", &last_modified_hdr)) |
139 info->last_modified = last_modified_hdr; | 141 info->last_modified = last_modified_hdr; |
140 if (headers->EnumerateHeader(NULL, "ETag", &etag)) | 142 if (headers->EnumerateHeader(NULL, "ETag", &etag)) |
(...skipping 11 matching lines...) Expand all Loading... |
152 "Accept-Ranges", | 154 "Accept-Ranges", |
153 &accept_ranges_)) { | 155 &accept_ranges_)) { |
154 accept_ranges_ = ""; | 156 accept_ranges_ = ""; |
155 } | 157 } |
156 | 158 |
157 info->prompt_user_for_save_location = | 159 info->prompt_user_for_save_location = |
158 save_info_.prompt_for_save_location && save_info_.file_path.empty(); | 160 save_info_.prompt_for_save_location && save_info_.file_path.empty(); |
159 info->referrer_charset = request_->context()->referrer_charset(); | 161 info->referrer_charset = request_->context()->referrer_charset(); |
160 info->save_info = save_info_; | 162 info->save_info = save_info_; |
161 | 163 |
162 | |
163 BrowserThread::PostTask( | 164 BrowserThread::PostTask( |
164 BrowserThread::UI, FROM_HERE, | 165 BrowserThread::UI, FROM_HERE, |
165 base::Bind(&DownloadResourceHandler::StartOnUIThread, this, | 166 base::Bind(&DownloadResourceHandler::StartOnUIThread, this, |
166 base::Passed(&info), request_handle)); | 167 base::Passed(&info), request_handle)); |
167 | 168 |
168 // We can't start saving the data before we create the file on disk and have a | |
169 // download id. The request will be un-paused in | |
170 // DownloadFileManager::CreateDownloadFile. | |
171 ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, | |
172 global_id_.request_id, | |
173 true); | |
174 | |
175 return true; | 169 return true; |
176 } | 170 } |
177 | 171 |
178 void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { | 172 void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { |
179 if (started_cb_.is_null()) | 173 if (started_cb_.is_null()) |
180 return; | 174 return; |
181 BrowserThread::PostTask( | 175 BrowserThread::PostTask( |
182 BrowserThread::UI, FROM_HERE, | 176 BrowserThread::UI, FROM_HERE, |
183 base::Bind(&CallStartedCBOnUIThread, started_cb_, id, error)); | 177 base::Bind(&CallStartedCBOnUIThread, started_cb_, id, error)); |
184 started_cb_.Reset(); | 178 started_cb_.Reset(); |
(...skipping 13 matching lines...) Expand all Loading... |
198 if (!read_buffer_) { | 192 if (!read_buffer_) { |
199 *buf_size = min_size < 0 ? kReadBufSize : min_size; | 193 *buf_size = min_size < 0 ? kReadBufSize : min_size; |
200 last_buffer_size_ = *buf_size; | 194 last_buffer_size_ = *buf_size; |
201 read_buffer_ = new net::IOBuffer(*buf_size); | 195 read_buffer_ = new net::IOBuffer(*buf_size); |
202 } | 196 } |
203 *buf = read_buffer_.get(); | 197 *buf = read_buffer_.get(); |
204 return true; | 198 return true; |
205 } | 199 } |
206 | 200 |
207 // Pass the buffer to the download file writer. | 201 // Pass the buffer to the download file writer. |
208 bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { | 202 bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read, |
| 203 bool* defer) { |
| 204 if (!read_buffer_) { |
| 205 // Ignore spurious OnReadCompleted! Deferring from OnReadCompleted tells |
| 206 // the ResourceDispatcherHost that we did not consume the data. |
| 207 // ResumeDeferredRequest then repeats the last OnReadCompleted call. |
| 208 // TODO(darin): Fix the ResourceDispatcherHost to avoid this hack! |
| 209 return true; |
| 210 } |
| 211 |
| 212 if (pause_count_ > 0) { |
| 213 *defer = was_deferred_ = true; |
| 214 return true; |
| 215 } |
| 216 |
| 217 if (download_id_ == DownloadId::Invalid()) { |
| 218 // We can't start saving the data before we create the file on disk and |
| 219 // have a download id. The request will be un-paused in |
| 220 // DownloadFileManager::CreateDownloadFile. |
| 221 *defer = was_deferred_ = true; |
| 222 return true; |
| 223 } |
| 224 |
209 base::TimeTicks now(base::TimeTicks::Now()); | 225 base::TimeTicks now(base::TimeTicks::Now()); |
210 if (!last_read_time_.is_null()) { | 226 if (!last_read_time_.is_null()) { |
211 double seconds_since_last_read = (now - last_read_time_).InSecondsF(); | 227 double seconds_since_last_read = (now - last_read_time_).InSecondsF(); |
212 if (now == last_read_time_) | 228 if (now == last_read_time_) |
213 // Use 1/10 ms as a "very small number" so that we avoid | 229 // Use 1/10 ms as a "very small number" so that we avoid |
214 // divide-by-zero error and still record a very high potential bandwidth. | 230 // divide-by-zero error and still record a very high potential bandwidth. |
215 seconds_since_last_read = 0.00001; | 231 seconds_since_last_read = 0.00001; |
216 | 232 |
217 double actual_bandwidth = (*bytes_read)/seconds_since_last_read; | 233 double actual_bandwidth = (*bytes_read)/seconds_since_last_read; |
218 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; | 234 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; |
(...skipping 14 matching lines...) Expand all Loading... |
233 // We are passing ownership of this buffer to the download file manager. | 249 // We are passing ownership of this buffer to the download file manager. |
234 if (need_update) { | 250 if (need_update) { |
235 BrowserThread::PostTask( | 251 BrowserThread::PostTask( |
236 BrowserThread::FILE, FROM_HERE, | 252 BrowserThread::FILE, FROM_HERE, |
237 base::Bind(&DownloadFileManager::UpdateDownload, | 253 base::Bind(&DownloadFileManager::UpdateDownload, |
238 download_file_manager_, download_id_, buffer_)); | 254 download_file_manager_, download_id_, buffer_)); |
239 } | 255 } |
240 | 256 |
241 // We schedule a pause outside of the read loop if there is too much file | 257 // We schedule a pause outside of the read loop if there is too much file |
242 // writing work to do. | 258 // writing work to do. |
243 if (vector_size > kLoadsToWrite) | 259 if (vector_size > kLoadsToWrite) { |
244 StartPauseTimer(); | 260 *defer = was_deferred_ = true; |
| 261 CheckWriteProgressLater(); |
| 262 } |
245 | 263 |
246 return true; | 264 return true; |
247 } | 265 } |
248 | 266 |
249 bool DownloadResourceHandler::OnResponseCompleted( | 267 bool DownloadResourceHandler::OnResponseCompleted( |
250 int request_id, | 268 int request_id, |
251 const net::URLRequestStatus& status, | 269 const net::URLRequestStatus& status, |
252 const std::string& security_info) { | 270 const std::string& security_info) { |
253 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 271 VLOG(20) << __FUNCTION__ << "()" << DebugString() |
254 << " request_id = " << request_id | 272 << " request_id = " << request_id |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 const DownloadRequestHandle& handle) { | 366 const DownloadRequestHandle& handle) { |
349 DownloadManager* download_manager = handle.GetDownloadManager(); | 367 DownloadManager* download_manager = handle.GetDownloadManager(); |
350 if (!download_manager) { | 368 if (!download_manager) { |
351 // NULL in unittests or if the page closed right after starting the | 369 // NULL in unittests or if the page closed right after starting the |
352 // download. | 370 // download. |
353 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); | 371 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); |
354 return; | 372 return; |
355 } | 373 } |
356 DownloadId download_id = download_manager->delegate()->GetNextId(); | 374 DownloadId download_id = download_manager->delegate()->GetNextId(); |
357 info->download_id = download_id; | 375 info->download_id = download_id; |
| 376 |
| 377 // NOTE: StartDownload triggers creation of the download destination file |
| 378 // that will hold the downloaded data. SetDownloadID unblocks the |
| 379 // DownloadResourceHandler to begin forwarding network data to the download |
| 380 // destination file. The sequence of these two steps is critical as creation |
| 381 // of the downloaded destination file has to happen before we attempt to |
| 382 // append data to it. Both of those operations happen on the FILE thread. |
| 383 |
| 384 download_file_manager_->StartDownload(info.release(), handle); |
| 385 |
358 BrowserThread::PostTask( | 386 BrowserThread::PostTask( |
359 BrowserThread::IO, FROM_HERE, | 387 BrowserThread::IO, FROM_HERE, |
360 base::Bind(&DownloadResourceHandler::set_download_id, this, | 388 base::Bind(&DownloadResourceHandler::SetDownloadID, this, |
361 info->download_id)); | 389 download_id)); |
362 // It's safe to continue on with download initiation before we have | 390 |
363 // confirmation that that download_id_ has been set on the IO thread, as any | |
364 // messages generated by the UI thread that affect the IO thread will be | |
365 // behind the message posted above. | |
366 download_file_manager_->StartDownload(info.release(), handle); | |
367 CallStartedCB(download_id, net::OK); | 391 CallStartedCB(download_id, net::OK); |
368 } | 392 } |
369 | 393 |
370 void DownloadResourceHandler::set_download_id(content::DownloadId id) { | 394 void DownloadResourceHandler::SetDownloadID(content::DownloadId id) { |
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 396 |
372 download_id_ = id; | 397 download_id_ = id; |
| 398 MaybeResumeRequest(); |
373 } | 399 } |
374 | 400 |
375 // If the content-length header is not present (or contains something other | 401 // If the content-length header is not present (or contains something other |
376 // than numbers), the incoming content_length is -1 (unknown size). | 402 // than numbers), the incoming content_length is -1 (unknown size). |
377 // Set the content length to 0 to indicate unknown size to DownloadManager. | 403 // Set the content length to 0 to indicate unknown size to DownloadManager. |
378 void DownloadResourceHandler::set_content_length(const int64& content_length) { | 404 void DownloadResourceHandler::SetContentLength(const int64& content_length) { |
379 content_length_ = 0; | 405 content_length_ = 0; |
380 if (content_length > 0) | 406 if (content_length > 0) |
381 content_length_ = content_length; | 407 content_length_ = content_length; |
382 } | 408 } |
383 | 409 |
384 void DownloadResourceHandler::set_content_disposition( | 410 void DownloadResourceHandler::SetContentDisposition( |
385 const std::string& content_disposition) { | 411 const std::string& content_disposition) { |
386 content_disposition_ = content_disposition; | 412 content_disposition_ = content_disposition; |
387 } | 413 } |
388 | 414 |
389 void DownloadResourceHandler::CheckWriteProgress() { | 415 void DownloadResourceHandler::CheckWriteProgress() { |
390 if (!buffer_.get()) | 416 if (!buffer_.get()) |
391 return; // The download completed while we were waiting to run. | 417 return; // The download completed while we were waiting to run. |
392 | 418 |
393 size_t contents_size = buffer_->size(); | 419 if (buffer_->size() > kLoadsToWrite) { |
| 420 // We'll come back later and see if it's okay to unpause the request. |
| 421 CheckWriteProgressLater(); |
| 422 return; |
| 423 } |
394 | 424 |
395 bool should_pause = contents_size > kLoadsToWrite; | 425 MaybeResumeRequest(); |
396 | |
397 // We'll come back later and see if it's okay to unpause the request. | |
398 if (should_pause) | |
399 StartPauseTimer(); | |
400 | |
401 if (is_paused_ != should_pause) { | |
402 ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, | |
403 global_id_.request_id, | |
404 should_pause); | |
405 is_paused_ = should_pause; | |
406 } | |
407 } | 426 } |
408 | 427 |
409 DownloadResourceHandler::~DownloadResourceHandler() { | 428 void DownloadResourceHandler::PauseRequest() { |
410 // This won't do anything if the callback was called before. | 429 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
411 // If it goes through, it will likely be because OnWillStart() returned | 430 |
412 // false somewhere in the chain of resource handlers. | 431 ++pause_count_; |
413 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); | |
414 } | 432 } |
415 | 433 |
416 void DownloadResourceHandler::StartPauseTimer() { | 434 void DownloadResourceHandler::ResumeRequest() { |
417 if (!pause_timer_.IsRunning()) | 435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
418 pause_timer_.Start(FROM_HERE, | 436 DCHECK_LT(0, pause_count_); |
419 base::TimeDelta::FromMilliseconds(kThrottleTimeMs), this, | 437 |
420 &DownloadResourceHandler::CheckWriteProgress); | 438 --pause_count_; |
| 439 MaybeResumeRequest(); |
| 440 } |
| 441 |
| 442 void DownloadResourceHandler::CancelRequest() { |
| 443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 444 |
| 445 ResourceDispatcherHostImpl::Get()->CancelRequest( |
| 446 global_id_.child_id, |
| 447 global_id_.request_id, |
| 448 false); |
421 } | 449 } |
422 | 450 |
423 std::string DownloadResourceHandler::DebugString() const { | 451 std::string DownloadResourceHandler::DebugString() const { |
424 return base::StringPrintf("{" | 452 return base::StringPrintf("{" |
425 " url_ = " "\"%s\"" | 453 " url_ = " "\"%s\"" |
426 " download_id_ = " "%d" | 454 " download_id_ = " "%d" |
427 " global_id_ = {" | 455 " global_id_ = {" |
428 " child_id = " "%d" | 456 " child_id = " "%d" |
429 " request_id = " "%d" | 457 " request_id = " "%d" |
430 " }" | 458 " }" |
431 " render_view_id_ = " "%d" | 459 " render_view_id_ = " "%d" |
432 " save_info_.file_path = \"%" PRFilePath "\"" | 460 " save_info_.file_path = \"%" PRFilePath "\"" |
433 " }", | 461 " }", |
434 request_ ? | 462 request_ ? |
435 request_->url().spec().c_str() : | 463 request_->url().spec().c_str() : |
436 "<NULL request>", | 464 "<NULL request>", |
437 download_id_.local(), | 465 download_id_.local(), |
438 global_id_.child_id, | 466 global_id_.child_id, |
439 global_id_.request_id, | 467 global_id_.request_id, |
440 render_view_id_, | 468 render_view_id_, |
441 save_info_.file_path.value().c_str()); | 469 save_info_.file_path.value().c_str()); |
442 } | 470 } |
| 471 |
| 472 DownloadResourceHandler::~DownloadResourceHandler() { |
| 473 // This won't do anything if the callback was called before. |
| 474 // If it goes through, it will likely be because OnWillStart() returned |
| 475 // false somewhere in the chain of resource handlers. |
| 476 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); |
| 477 } |
| 478 |
| 479 void DownloadResourceHandler::CheckWriteProgressLater() { |
| 480 if (!check_write_progress_timer_.IsRunning()) { |
| 481 check_write_progress_timer_.Start( |
| 482 FROM_HERE, |
| 483 base::TimeDelta::FromMilliseconds(kThrottleTimeMs), |
| 484 this, |
| 485 &DownloadResourceHandler::CheckWriteProgress); |
| 486 } |
| 487 } |
| 488 |
| 489 void DownloadResourceHandler::MaybeResumeRequest() { |
| 490 if (!was_deferred_) |
| 491 return; |
| 492 |
| 493 if (pause_count_ > 0) |
| 494 return; |
| 495 if (download_id_ == DownloadId::Invalid()) |
| 496 return; |
| 497 if (buffer_.get() && (buffer_->size() > kLoadsToWrite)) |
| 498 return; |
| 499 |
| 500 was_deferred_ = false; |
| 501 ResourceDispatcherHostImpl::Get()->ResumeDeferredRequest( |
| 502 global_id_.child_id, |
| 503 global_id_.request_id); |
| 504 } |
OLD | NEW |