download_resource_handler.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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/browser_thread.h" 14#include "chrome/browser/download/download_item.h" 15#include "chrome/browser/download/download_file_manager.h" 16#include "chrome/browser/history/download_create_info.h" 17#include "chrome/browser/renderer_host/global_request_id.h" 18#include "chrome/browser/renderer_host/resource_dispatcher_host.h" 19#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" 20#include "chrome/common/resource_response.h" 21#include "net/base/io_buffer.h" 22#include "net/http/http_response_headers.h" 23#include "net/url_request/url_request_context.h" 24 25DownloadResourceHandler::DownloadResourceHandler( 26 ResourceDispatcherHost* rdh, 27 int render_process_host_id, 28 int render_view_id, 29 int request_id, 30 const GURL& url, 31 DownloadFileManager* download_file_manager, 32 net::URLRequest* request, 33 bool save_as, 34 const DownloadSaveInfo& save_info) 35 : download_id_(-1), 36 global_id_(render_process_host_id, request_id), 37 render_view_id_(render_view_id), 38 url_(url), 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 url_check_pending_(false) { 48} 49 50bool 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. 57bool DownloadResourceHandler::OnRequestRedirected(int request_id, 58 const GURL& url, 59 ResourceResponse* response, 60 bool* defer) { 61 url_ = url; 62 return true; 63} 64 65// Callback when the result of checking a download URL is known. 66// TODO(lzheng): We should create a information bar with buttons to ask 67// if users want to proceed when the download is malicious. 68void DownloadResourceHandler::OnDownloadUrlCheckResult( 69 const GURL& url, SafeBrowsingService::UrlCheckResult result) { 70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 71 DCHECK(url_check_pending_); 72 73 UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration", 74 base::TimeTicks::Now() - download_start_time_); 75 76 if (result == SafeBrowsingService::BINARY_MALWARE) { 77 // TODO(lzheng): More UI work to show warnings properly on download shelf. 78 DLOG(WARNING) << "This url leads to a malware downloading: " 79 << url.spec(); 80 UpdateDownloadUrlCheckStats(DOWNLOAD_URL_CHECKS_MALWARE); 81 } 82 83 url_check_pending_ = false; 84 // Note: Release() should be the last line in this call. It is for 85 // the AddRef in CheckSafeBrowsing. 86 Release(); 87} 88 89// Send the download creation information to the download thread. 90void DownloadResourceHandler::StartDownloadUrlCheck() { 91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 92 AddRef(); 93 if (!rdh_->safe_browsing_service()->CheckDownloadUrl(url_, this)) { 94 url_check_pending_ = true; 95 UpdateDownloadUrlCheckStats(DOWNLOAD_URL_CHECKS_TOTAL); 96 // Note: in this case, the AddRef will be balanced in 97 // "OnDownloadUrlCheckResult" or "OnRequestClosed". 98 } else { 99 // Immediately release the AddRef() at the beginning of this function 100 // since no more callbacks will happen. 101 Release(); 102 DVLOG(1) << "url: " << url_.spec() << " is safe to download."; 103 } 104} 105 106// Send the download creation information to the download thread. 107bool DownloadResourceHandler::OnResponseStarted(int request_id, 108 ResourceResponse* response) { 109 VLOG(20) << __FUNCTION__ << "()" << DebugString() 110 << " request_id = " << request_id; 111 DCHECK(!url_check_pending_); 112 download_start_time_ = base::TimeTicks::Now(); 113 StartDownloadUrlCheck(); 114 std::string content_disposition; 115 request_->GetResponseHeaderByName("content-disposition", 116 &content_disposition); 117 set_content_disposition(content_disposition); 118 set_content_length(response->response_head.content_length); 119 120 const ResourceDispatcherHostRequestInfo* request_info = 121 ResourceDispatcherHost::InfoForRequest(request_); 122 123 download_id_ = download_file_manager_->GetNextId(); 124 // |download_file_manager_| consumes (deletes): 125 DownloadCreateInfo* info = new DownloadCreateInfo; 126 info->url = url_; 127 info->referrer_url = GURL(request_->referrer()); 128 info->start_time = base::Time::Now(); 129 info->received_bytes = 0; 130 info->total_bytes = content_length_; 131 info->state = DownloadItem::IN_PROGRESS; 132 info->download_id = download_id_; 133 info->has_user_gesture = request_info->has_user_gesture(); 134 info->child_id = global_id_.child_id; 135 info->render_view_id = render_view_id_; 136 info->request_id = global_id_.request_id; 137 info->content_disposition = content_disposition_; 138 info->mime_type = response->response_head.mime_type; 139 140 std::string content_type_header; 141 if (!response->response_head.headers || 142 !response->response_head.headers->GetMimeType(&content_type_header)) 143 content_type_header = ""; 144 info->original_mime_type = content_type_header; 145 146 info->prompt_user_for_save_location = 147 save_as_ && save_info_.file_path.empty(); 148 info->is_dangerous = false; 149 info->referrer_charset = request_->context()->referrer_charset(); 150 info->save_info = save_info_; 151 BrowserThread::PostTask( 152 BrowserThread::UI, FROM_HERE, 153 NewRunnableMethod( 154 download_file_manager_, &DownloadFileManager::StartDownload, info)); 155 156 // We can't start saving the data before we create the file on disk. 157 // The request will be un-paused in DownloadFileManager::CreateDownloadFile. 158 rdh_->PauseRequest(global_id_.child_id, global_id_.request_id, true); 159 160 return true; 161} 162 163bool DownloadResourceHandler::OnWillStart(int request_id, 164 const GURL& url, 165 bool* defer) { 166 return true; 167} 168 169// Create a new buffer, which will be handed to the download thread for file 170// writing and deletion. 171bool DownloadResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, 172 int* buf_size, int min_size) { 173 DCHECK(buf && buf_size); 174 if (!read_buffer_) { 175 *buf_size = min_size < 0 ? kReadBufSize : min_size; 176 read_buffer_ = new net::IOBuffer(*buf_size); 177 } 178 *buf = read_buffer_.get(); 179 return true; 180} 181 182// Pass the buffer to the download file writer. 183bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { 184 if (!*bytes_read) 185 return true; 186 DCHECK(read_buffer_); 187 AutoLock auto_lock(buffer_->lock); 188 bool need_update = buffer_->contents.empty(); 189 190 // We are passing ownership of this buffer to the download file manager. 191 net::IOBuffer* buffer = NULL; 192 read_buffer_.swap(&buffer); 193 buffer_->contents.push_back(std::make_pair(buffer, *bytes_read)); 194 if (need_update) { 195 BrowserThread::PostTask( 196 BrowserThread::FILE, FROM_HERE, 197 NewRunnableMethod(download_file_manager_, 198 &DownloadFileManager::UpdateDownload, 199 download_id_, 200 buffer_)); 201 } 202 203 // We schedule a pause outside of the read loop if there is too much file 204 // writing work to do. 205 if (buffer_->contents.size() > kLoadsToWrite) 206 StartPauseTimer(); 207 208 return true; 209} 210 211bool DownloadResourceHandler::OnResponseCompleted( 212 int request_id, 213 const URLRequestStatus& status, 214 const std::string& security_info) { 215 VLOG(20) << __FUNCTION__ << "()" << DebugString() 216 << " request_id = " << request_id 217 << " status.status() = " << status.status() 218 << " status.os_error() = " << status.os_error(); 219 BrowserThread::PostTask( 220 BrowserThread::FILE, FROM_HERE, 221 NewRunnableMethod(download_file_manager_, 222 &DownloadFileManager::OnResponseCompleted, 223 download_id_, 224 buffer_)); 225 read_buffer_ = NULL; 226 227 // 'buffer_' is deleted by the DownloadFileManager. 228 buffer_ = NULL; 229 return true; 230} 231 232void DownloadResourceHandler::OnRequestClosed() { 233 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", 234 base::TimeTicks::Now() - download_start_time_); 235 if (url_check_pending_) { 236 DVLOG(1) << "Cancel pending download url checking request: " << this; 237 rdh_->safe_browsing_service()->CancelCheck(this); 238 UpdateDownloadUrlCheckStats(DOWNLOAD_URL_CHECKS_CANCELED); 239 url_check_pending_ = false; 240 // Balance the AddRef() from StartDownloadUrlCheck() which would usually be 241 // balanced by OnDownloadUrlCheckResult(). 242 Release(); 243 } 244} 245 246// If the content-length header is not present (or contains something other 247// than numbers), the incoming content_length is -1 (unknown size). 248// Set the content length to 0 to indicate unknown size to DownloadManager. 249void DownloadResourceHandler::set_content_length(const int64& content_length) { 250 content_length_ = 0; 251 if (content_length > 0) 252 content_length_ = content_length; 253} 254 255void DownloadResourceHandler::set_content_disposition( 256 const std::string& content_disposition) { 257 content_disposition_ = content_disposition; 258} 259 260void DownloadResourceHandler::CheckWriteProgress() { 261 if (!buffer_) 262 return; // The download completed while we were waiting to run. 263 264 size_t contents_size; 265 { 266 AutoLock lock(buffer_->lock); 267 contents_size = buffer_->contents.size(); 268 } 269 270 bool should_pause = contents_size > kLoadsToWrite; 271 272 // We'll come back later and see if it's okay to unpause the request. 273 if (should_pause) 274 StartPauseTimer(); 275 276 if (is_paused_ != should_pause) { 277 rdh_->PauseRequest(global_id_.child_id, global_id_.request_id, 278 should_pause); 279 is_paused_ = should_pause; 280 } 281} 282 283DownloadResourceHandler::~DownloadResourceHandler() { 284} 285 286void DownloadResourceHandler::StartPauseTimer() { 287 if (!pause_timer_.IsRunning()) 288 pause_timer_.Start(base::TimeDelta::FromMilliseconds(kThrottleTimeMs), this, 289 &DownloadResourceHandler::CheckWriteProgress); 290} 291 292std::string DownloadResourceHandler::DebugString() const { 293 return base::StringPrintf("{" 294 " url_ = " "\"%s\"" 295 " download_id_ = " "%d" 296 " global_id_ = {" 297 " child_id = " "%d" 298 " request_id = " "%d" 299 " }" 300 " render_view_id_ = " "%d" 301 " save_info_.file_path = \"%" PRFilePath "\"" 302 " }", 303 url_.spec().c_str(), 304 download_id_, 305 global_id_.child_id, 306 global_id_.request_id, 307 render_view_id_, 308 save_info_.file_path.value().c_str()); 309} 310 311void DownloadResourceHandler::UpdateDownloadUrlCheckStats( 312 SBStatsType stat_type) { 313 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadUrlChecks", 314 stat_type, 315 DOWNLOAD_URL_CHECKS_MAX); 316} 317