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