• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/renderer_host/download_resource_handler.h"
6 
7 #include <string>
8 
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/metrics/stats_counters.h"
12 #include "base/stringprintf.h"
13 #include "chrome/browser/download/download_item.h"
14 #include "chrome/browser/download/download_file_manager.h"
15 #include "chrome/browser/download/download_util.h"
16 #include "chrome/browser/history/download_create_info.h"
17 #include "content/browser/browser_thread.h"
18 #include "content/browser/renderer_host/global_request_id.h"
19 #include "content/browser/renderer_host/resource_dispatcher_host.h"
20 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
21 #include "content/common/resource_response.h"
22 #include "net/base/io_buffer.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/url_request/url_request_context.h"
25 
DownloadResourceHandler(ResourceDispatcherHost * rdh,int render_process_host_id,int render_view_id,int request_id,const GURL & url,DownloadFileManager * download_file_manager,net::URLRequest * request,bool save_as,const DownloadSaveInfo & save_info)26 DownloadResourceHandler::DownloadResourceHandler(
27     ResourceDispatcherHost* rdh,
28     int render_process_host_id,
29     int render_view_id,
30     int request_id,
31     const GURL& url,
32     DownloadFileManager* download_file_manager,
33     net::URLRequest* request,
34     bool save_as,
35     const DownloadSaveInfo& save_info)
36     : download_id_(-1),
37       global_id_(render_process_host_id, request_id),
38       render_view_id_(render_view_id),
39       content_length_(0),
40       download_file_manager_(download_file_manager),
41       request_(request),
42       save_as_(save_as),
43       save_info_(save_info),
44       buffer_(new DownloadBuffer),
45       rdh_(rdh),
46       is_paused_(false) {
47   download_util::RecordDownloadCount(download_util::UNTHROTTLED_COUNT);
48 }
49 
OnUploadProgress(int request_id,uint64 position,uint64 size)50 bool DownloadResourceHandler::OnUploadProgress(int request_id,
51                                                uint64 position,
52                                                uint64 size) {
53   return true;
54 }
55 
56 // Not needed, as this event handler ought to be the final resource.
OnRequestRedirected(int request_id,const GURL & url,ResourceResponse * response,bool * defer)57 bool DownloadResourceHandler::OnRequestRedirected(int request_id,
58                                                   const GURL& url,
59                                                   ResourceResponse* response,
60                                                   bool* defer) {
61   return true;
62 }
63 
64 // Send the download creation information to the download thread.
OnResponseStarted(int request_id,ResourceResponse * response)65 bool DownloadResourceHandler::OnResponseStarted(int request_id,
66                                                 ResourceResponse* response) {
67   VLOG(20) << __FUNCTION__ << "()" << DebugString()
68            << " request_id = " << request_id;
69   download_start_time_ = base::TimeTicks::Now();
70   std::string content_disposition;
71   request_->GetResponseHeaderByName("content-disposition",
72                                     &content_disposition);
73   set_content_disposition(content_disposition);
74   set_content_length(response->response_head.content_length);
75 
76   const ResourceDispatcherHostRequestInfo* request_info =
77     ResourceDispatcherHost::InfoForRequest(request_);
78 
79   download_id_ = download_file_manager_->GetNextId();
80 
81   // Deleted in DownloadManager.
82   DownloadCreateInfo* info = new DownloadCreateInfo;
83   info->url_chain = request_->url_chain();
84   info->referrer_url = GURL(request_->referrer());
85   info->start_time = base::Time::Now();
86   info->received_bytes = 0;
87   info->total_bytes = content_length_;
88   info->state = DownloadItem::IN_PROGRESS;
89   info->download_id = download_id_;
90   info->has_user_gesture = request_info->has_user_gesture();
91   info->child_id = global_id_.child_id;
92   info->render_view_id = render_view_id_;
93   info->request_id = global_id_.request_id;
94   info->content_disposition = content_disposition_;
95   info->mime_type = response->response_head.mime_type;
96   // TODO(ahendrickson) -- Get the last modified time and etag, so we can
97   // resume downloading.
98 
99   std::string content_type_header;
100   if (!response->response_head.headers ||
101       !response->response_head.headers->GetMimeType(&content_type_header))
102     content_type_header = "";
103   info->original_mime_type = content_type_header;
104 
105   info->prompt_user_for_save_location =
106       save_as_ && save_info_.file_path.empty();
107   info->is_dangerous_file = false;
108   info->is_dangerous_url = false;
109   info->referrer_charset = request_->context()->referrer_charset();
110   info->save_info = save_info_;
111   BrowserThread::PostTask(
112       BrowserThread::UI, FROM_HERE,
113       NewRunnableMethod(
114           download_file_manager_, &DownloadFileManager::StartDownload, info));
115 
116   // We can't start saving the data before we create the file on disk.
117   // The request will be un-paused in DownloadFileManager::CreateDownloadFile.
118   rdh_->PauseRequest(global_id_.child_id, global_id_.request_id, true);
119 
120   return true;
121 }
122 
OnWillStart(int request_id,const GURL & url,bool * defer)123 bool DownloadResourceHandler::OnWillStart(int request_id,
124                                           const GURL& url,
125                                           bool* defer) {
126   return true;
127 }
128 
129 // Create a new buffer, which will be handed to the download thread for file
130 // writing and deletion.
OnWillRead(int request_id,net::IOBuffer ** buf,int * buf_size,int min_size)131 bool DownloadResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
132                                          int* buf_size, int min_size) {
133   DCHECK(buf && buf_size);
134   if (!read_buffer_) {
135     *buf_size = min_size < 0 ? kReadBufSize : min_size;
136     read_buffer_ = new net::IOBuffer(*buf_size);
137   }
138   *buf = read_buffer_.get();
139   return true;
140 }
141 
142 // Pass the buffer to the download file writer.
OnReadCompleted(int request_id,int * bytes_read)143 bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
144   if (!*bytes_read)
145     return true;
146   DCHECK(read_buffer_);
147   base::AutoLock auto_lock(buffer_->lock);
148   bool need_update = buffer_->contents.empty();
149 
150   // We are passing ownership of this buffer to the download file manager.
151   net::IOBuffer* buffer = NULL;
152   read_buffer_.swap(&buffer);
153   buffer_->contents.push_back(std::make_pair(buffer, *bytes_read));
154   if (need_update) {
155     BrowserThread::PostTask(
156         BrowserThread::FILE, FROM_HERE,
157         NewRunnableMethod(download_file_manager_,
158                           &DownloadFileManager::UpdateDownload,
159                           download_id_,
160                           buffer_.get()));
161   }
162 
163   // We schedule a pause outside of the read loop if there is too much file
164   // writing work to do.
165   if (buffer_->contents.size() > kLoadsToWrite)
166     StartPauseTimer();
167 
168   return true;
169 }
170 
OnResponseCompleted(int request_id,const net::URLRequestStatus & status,const std::string & security_info)171 bool DownloadResourceHandler::OnResponseCompleted(
172     int request_id,
173     const net::URLRequestStatus& status,
174     const std::string& security_info) {
175   VLOG(20) << __FUNCTION__ << "()" << DebugString()
176            << " request_id = " << request_id
177            << " status.status() = " << status.status()
178            << " status.os_error() = " << status.os_error();
179   int error_code = (status.status() == net::URLRequestStatus::FAILED) ?
180       status.os_error() : 0;
181   // We transfer ownership to |DownloadFileManager| to delete |buffer_|,
182   // so that any functions queued up on the FILE thread are executed
183   // before deletion.
184   BrowserThread::PostTask(
185       BrowserThread::FILE, FROM_HERE,
186       NewRunnableMethod(download_file_manager_,
187                         &DownloadFileManager::OnResponseCompleted,
188                         download_id_,
189                         buffer_.release(),
190                         error_code,
191                         security_info));
192   read_buffer_ = NULL;
193   return true;
194 }
195 
OnRequestClosed()196 void DownloadResourceHandler::OnRequestClosed() {
197   UMA_HISTOGRAM_TIMES("SB2.DownloadDuration",
198                       base::TimeTicks::Now() - download_start_time_);
199 }
200 
201 // If the content-length header is not present (or contains something other
202 // than numbers), the incoming content_length is -1 (unknown size).
203 // Set the content length to 0 to indicate unknown size to DownloadManager.
set_content_length(const int64 & content_length)204 void DownloadResourceHandler::set_content_length(const int64& content_length) {
205   content_length_ = 0;
206   if (content_length > 0)
207     content_length_ = content_length;
208 }
209 
set_content_disposition(const std::string & content_disposition)210 void DownloadResourceHandler::set_content_disposition(
211     const std::string& content_disposition) {
212   content_disposition_ = content_disposition;
213 }
214 
CheckWriteProgress()215 void DownloadResourceHandler::CheckWriteProgress() {
216   if (!buffer_.get())
217     return;  // The download completed while we were waiting to run.
218 
219   size_t contents_size;
220   {
221     base::AutoLock lock(buffer_->lock);
222     contents_size = buffer_->contents.size();
223   }
224 
225   bool should_pause = contents_size > kLoadsToWrite;
226 
227   // We'll come back later and see if it's okay to unpause the request.
228   if (should_pause)
229     StartPauseTimer();
230 
231   if (is_paused_ != should_pause) {
232     rdh_->PauseRequest(global_id_.child_id, global_id_.request_id,
233                        should_pause);
234     is_paused_ = should_pause;
235   }
236 }
237 
~DownloadResourceHandler()238 DownloadResourceHandler::~DownloadResourceHandler() {
239 }
240 
StartPauseTimer()241 void DownloadResourceHandler::StartPauseTimer() {
242   if (!pause_timer_.IsRunning())
243     pause_timer_.Start(base::TimeDelta::FromMilliseconds(kThrottleTimeMs), this,
244                        &DownloadResourceHandler::CheckWriteProgress);
245 }
246 
DebugString() const247 std::string DownloadResourceHandler::DebugString() const {
248   return base::StringPrintf("{"
249                             " url_ = " "\"%s\""
250                             " download_id_ = " "%d"
251                             " global_id_ = {"
252                             " child_id = " "%d"
253                             " request_id = " "%d"
254                             " }"
255                             " render_view_id_ = " "%d"
256                             " save_info_.file_path = \"%" PRFilePath "\""
257                             " }",
258                             request_->url().spec().c_str(),
259                             download_id_,
260                             global_id_.child_id,
261                             global_id_.request_id,
262                             render_view_id_,
263                             save_info_.file_path.value().c_str());
264 }
265