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 3// found in the LICENSE file. 4 5#include "content/browser/download/download_resource_handler.h" 6 7#include <string> 8 9#include "base/bind.h" 10#include "base/logging.h" 11#include "base/message_loop/message_loop_proxy.h" 12#include "base/metrics/histogram.h" 13#include "base/metrics/stats_counters.h" 14#include "base/strings/stringprintf.h" 15#include "content/browser/byte_stream.h" 16#include "content/browser/download/download_create_info.h" 17#include "content/browser/download/download_interrupt_reasons_impl.h" 18#include "content/browser/download/download_manager_impl.h" 19#include "content/browser/download/download_request_handle.h" 20#include "content/browser/download/download_stats.h" 21#include "content/browser/loader/resource_dispatcher_host_impl.h" 22#include "content/browser/loader/resource_request_info_impl.h" 23#include "content/public/browser/browser_thread.h" 24#include "content/public/browser/download_interrupt_reasons.h" 25#include "content/public/browser/download_item.h" 26#include "content/public/browser/download_manager_delegate.h" 27#include "content/public/browser/navigation_entry.h" 28#include "content/public/browser/power_save_blocker.h" 29#include "content/public/browser/web_contents.h" 30#include "content/public/common/resource_response.h" 31#include "net/base/io_buffer.h" 32#include "net/base/net_errors.h" 33#include "net/http/http_response_headers.h" 34#include "net/http/http_status_code.h" 35#include "net/url_request/url_request_context.h" 36 37namespace content { 38 39struct DownloadResourceHandler::DownloadTabInfo { 40 GURL tab_url; 41 GURL tab_referrer_url; 42}; 43 44namespace { 45 46void CallStartedCBOnUIThread( 47 const DownloadUrlParameters::OnStartedCallback& started_cb, 48 DownloadItem* item, 49 DownloadInterruptReason interrupt_reason) { 50 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 51 52 if (started_cb.is_null()) 53 return; 54 started_cb.Run(item, interrupt_reason); 55} 56 57// Static function in order to prevent any accidental accesses to 58// DownloadResourceHandler members from the UI thread. 59static void StartOnUIThread( 60 scoped_ptr<DownloadCreateInfo> info, 61 DownloadResourceHandler::DownloadTabInfo* tab_info, 62 scoped_ptr<ByteStreamReader> stream, 63 const DownloadUrlParameters::OnStartedCallback& started_cb) { 64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 65 66 DownloadManager* download_manager = info->request_handle.GetDownloadManager(); 67 if (!download_manager) { 68 // NULL in unittests or if the page closed right after starting the 69 // download. 70 if (!started_cb.is_null()) 71 started_cb.Run(NULL, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); 72 73 // |stream| gets deleted on non-FILE thread, but it's ok since 74 // we're not using stream_writer_ yet. 75 76 return; 77 } 78 79 info->tab_url = tab_info->tab_url; 80 info->tab_referrer_url = tab_info->tab_referrer_url; 81 82 download_manager->StartDownload(info.Pass(), stream.Pass(), started_cb); 83} 84 85void InitializeDownloadTabInfoOnUIThread( 86 const DownloadRequestHandle& request_handle, 87 DownloadResourceHandler::DownloadTabInfo* tab_info) { 88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 89 90 WebContents* web_contents = request_handle.GetWebContents(); 91 if (web_contents) { 92 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry(); 93 if (entry) { 94 tab_info->tab_url = entry->GetURL(); 95 tab_info->tab_referrer_url = entry->GetReferrer().url; 96 } 97 } 98} 99 100} // namespace 101 102const int DownloadResourceHandler::kDownloadByteStreamSize = 100 * 1024; 103 104DownloadResourceHandler::DownloadResourceHandler( 105 uint32 id, 106 net::URLRequest* request, 107 const DownloadUrlParameters::OnStartedCallback& started_cb, 108 scoped_ptr<DownloadSaveInfo> save_info) 109 : ResourceHandler(request), 110 download_id_(id), 111 started_cb_(started_cb), 112 save_info_(save_info.Pass()), 113 last_buffer_size_(0), 114 bytes_read_(0), 115 pause_count_(0), 116 was_deferred_(false), 117 on_response_started_called_(false) { 118 RecordDownloadCount(UNTHROTTLED_COUNT); 119 120 // Do UI thread initialization asap after DownloadResourceHandler creation 121 // since the tab could be navigated before StartOnUIThread gets called. 122 const ResourceRequestInfoImpl* request_info = GetRequestInfo(); 123 tab_info_ = new DownloadTabInfo(); 124 BrowserThread::PostTask( 125 BrowserThread::UI, 126 FROM_HERE, 127 base::Bind(&InitializeDownloadTabInfoOnUIThread, 128 DownloadRequestHandle(AsWeakPtr(), 129 request_info->GetChildID(), 130 request_info->GetRouteID(), 131 request_info->GetRequestID()), 132 tab_info_)); 133 power_save_blocker_ = PowerSaveBlocker::Create( 134 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, 135 "Download in progress"); 136} 137 138bool DownloadResourceHandler::OnUploadProgress(uint64 position, 139 uint64 size) { 140 return true; 141} 142 143bool DownloadResourceHandler::OnRequestRedirected( 144 const net::RedirectInfo& redirect_info, 145 ResourceResponse* response, 146 bool* defer) { 147 return true; 148} 149 150// Send the download creation information to the download thread. 151bool DownloadResourceHandler::OnResponseStarted( 152 ResourceResponse* response, 153 bool* defer) { 154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 155 // There can be only one (call) 156 DCHECK(!on_response_started_called_); 157 on_response_started_called_ = true; 158 159 VLOG(20) << __FUNCTION__ << "()" << DebugString(); 160 download_start_time_ = base::TimeTicks::Now(); 161 162 // If it's a download, we don't want to poison the cache with it. 163 request()->StopCaching(); 164 165 // Lower priority as well, so downloads don't contend for resources 166 // with main frames. 167 request()->SetPriority(net::IDLE); 168 169 // If the content-length header is not present (or contains something other 170 // than numbers), the incoming content_length is -1 (unknown size). 171 // Set the content length to 0 to indicate unknown size to DownloadManager. 172 int64 content_length = 173 response->head.content_length > 0 ? response->head.content_length : 0; 174 175 const ResourceRequestInfoImpl* request_info = GetRequestInfo(); 176 177 // Deleted in DownloadManager. 178 scoped_ptr<DownloadCreateInfo> info( 179 new DownloadCreateInfo(base::Time::Now(), 180 content_length, 181 request()->net_log(), 182 request_info->HasUserGesture(), 183 request_info->GetPageTransition(), 184 save_info_.Pass())); 185 186 // Create the ByteStream for sending data to the download sink. 187 scoped_ptr<ByteStreamReader> stream_reader; 188 CreateByteStream( 189 base::MessageLoopProxy::current(), 190 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), 191 kDownloadByteStreamSize, &stream_writer_, &stream_reader); 192 stream_writer_->RegisterCallback( 193 base::Bind(&DownloadResourceHandler::ResumeRequest, AsWeakPtr())); 194 195 info->download_id = download_id_; 196 info->url_chain = request()->url_chain(); 197 info->referrer_url = GURL(request()->referrer()); 198 info->mime_type = response->head.mime_type; 199 info->remote_address = request()->GetSocketAddress().host(); 200 request()->GetResponseHeaderByName("content-disposition", 201 &info->content_disposition); 202 RecordDownloadMimeType(info->mime_type); 203 RecordDownloadContentDisposition(info->content_disposition); 204 205 info->request_handle = 206 DownloadRequestHandle(AsWeakPtr(), request_info->GetChildID(), 207 request_info->GetRouteID(), 208 request_info->GetRequestID()); 209 210 // Get the last modified time and etag. 211 const net::HttpResponseHeaders* headers = request()->response_headers(); 212 if (headers) { 213 if (headers->HasStrongValidators()) { 214 // If we don't have strong validators as per RFC 2616 section 13.3.3, then 215 // we neither store nor use them for range requests. 216 if (!headers->EnumerateHeader(NULL, "Last-Modified", 217 &info->last_modified)) 218 info->last_modified.clear(); 219 if (!headers->EnumerateHeader(NULL, "ETag", &info->etag)) 220 info->etag.clear(); 221 } 222 223 int status = headers->response_code(); 224 if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { 225 // Success & not range response; if we asked for a range, we didn't 226 // get it--reset the file pointers to reflect that. 227 info->save_info->offset = 0; 228 info->save_info->hash_state = ""; 229 } 230 231 if (!headers->GetMimeType(&info->original_mime_type)) 232 info->original_mime_type.clear(); 233 } 234 235 // Blink verifies that the requester of this download is allowed to set a 236 // suggested name for the security origin of the downlaod URL. However, this 237 // assumption doesn't hold if there were cross origin redirects. Therefore, 238 // clear the suggested_name for such requests. 239 if (info->url_chain.size() > 1 && 240 info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) 241 info->save_info->suggested_name.clear(); 242 243 BrowserThread::PostTask( 244 BrowserThread::UI, FROM_HERE, 245 base::Bind(&StartOnUIThread, 246 base::Passed(&info), 247 base::Owned(tab_info_), 248 base::Passed(&stream_reader), 249 // Pass to StartOnUIThread so that variable 250 // access is always on IO thread but function 251 // is called on UI thread. 252 started_cb_)); 253 // Now owned by the task that was just posted. 254 tab_info_ = NULL; 255 // Guaranteed to be called in StartOnUIThread 256 started_cb_.Reset(); 257 258 return true; 259} 260 261void DownloadResourceHandler::CallStartedCB( 262 DownloadItem* item, 263 DownloadInterruptReason interrupt_reason) { 264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 265 if (started_cb_.is_null()) 266 return; 267 BrowserThread::PostTask( 268 BrowserThread::UI, 269 FROM_HERE, 270 base::Bind( 271 &CallStartedCBOnUIThread, started_cb_, item, interrupt_reason)); 272 started_cb_.Reset(); 273} 274 275bool DownloadResourceHandler::OnWillStart(const GURL& url, bool* defer) { 276 return true; 277} 278 279bool DownloadResourceHandler::OnBeforeNetworkStart(const GURL& url, 280 bool* defer) { 281 return true; 282} 283 284// Create a new buffer, which will be handed to the download thread for file 285// writing and deletion. 286bool DownloadResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, 287 int* buf_size, 288 int min_size) { 289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 290 DCHECK(buf && buf_size); 291 DCHECK(!read_buffer_.get()); 292 293 *buf_size = min_size < 0 ? kReadBufSize : min_size; 294 last_buffer_size_ = *buf_size; 295 read_buffer_ = new net::IOBuffer(*buf_size); 296 *buf = read_buffer_.get(); 297 return true; 298} 299 300// Pass the buffer to the download file writer. 301bool DownloadResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { 302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 303 DCHECK(read_buffer_.get()); 304 305 base::TimeTicks now(base::TimeTicks::Now()); 306 if (!last_read_time_.is_null()) { 307 double seconds_since_last_read = (now - last_read_time_).InSecondsF(); 308 if (now == last_read_time_) 309 // Use 1/10 ms as a "very small number" so that we avoid 310 // divide-by-zero error and still record a very high potential bandwidth. 311 seconds_since_last_read = 0.00001; 312 313 double actual_bandwidth = (bytes_read)/seconds_since_last_read; 314 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; 315 RecordBandwidth(actual_bandwidth, potential_bandwidth); 316 } 317 last_read_time_ = now; 318 319 if (!bytes_read) 320 return true; 321 bytes_read_ += bytes_read; 322 DCHECK(read_buffer_.get()); 323 324 // Take the data ship it down the stream. If the stream is full, pause the 325 // request; the stream callback will resume it. 326 if (!stream_writer_->Write(read_buffer_, bytes_read)) { 327 PauseRequest(); 328 *defer = was_deferred_ = true; 329 last_stream_pause_time_ = now; 330 } 331 332 read_buffer_ = NULL; // Drop our reference. 333 334 if (pause_count_ > 0) 335 *defer = was_deferred_ = true; 336 337 return true; 338} 339 340void DownloadResourceHandler::OnResponseCompleted( 341 const net::URLRequestStatus& status, 342 const std::string& security_info, 343 bool* defer) { 344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 345 int response_code = status.is_success() ? request()->GetResponseCode() : 0; 346 VLOG(20) << __FUNCTION__ << "()" << DebugString() 347 << " status.status() = " << status.status() 348 << " status.error() = " << status.error() 349 << " response_code = " << response_code; 350 351 net::Error error_code = net::OK; 352 if (status.status() == net::URLRequestStatus::FAILED || 353 // Note cancels as failures too. 354 status.status() == net::URLRequestStatus::CANCELED) { 355 error_code = static_cast<net::Error>(status.error()); // Normal case. 356 // Make sure that at least the fact of failure comes through. 357 if (error_code == net::OK) 358 error_code = net::ERR_FAILED; 359 } 360 361 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are 362 // allowed since a number of servers in the wild close the connection too 363 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - 364 // treat downloads as complete in both cases, so we follow their lead. 365 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || 366 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { 367 error_code = net::OK; 368 } 369 DownloadInterruptReason reason = 370 ConvertNetErrorToInterruptReason( 371 error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); 372 373 if (status.status() == net::URLRequestStatus::CANCELED && 374 status.error() == net::ERR_ABORTED) { 375 // CANCELED + ERR_ABORTED == something outside of the network 376 // stack cancelled the request. There aren't that many things that 377 // could do this to a download request (whose lifetime is separated from 378 // the tab from which it came). We map this to USER_CANCELLED as the 379 // case we know about (system suspend because of laptop close) corresponds 380 // to a user action. 381 // TODO(ahendrickson) -- Find a better set of codes to use here, as 382 // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. 383 if (net::IsCertStatusError(request()->ssl_info().cert_status)) 384 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; 385 else 386 reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; 387 } 388 389 if (status.is_success() && 390 reason == DOWNLOAD_INTERRUPT_REASON_NONE && 391 request()->response_headers()) { 392 // Handle server's response codes. 393 switch(response_code) { 394 case -1: // Non-HTTP request. 395 case net::HTTP_OK: 396 case net::HTTP_CREATED: 397 case net::HTTP_ACCEPTED: 398 case net::HTTP_NON_AUTHORITATIVE_INFORMATION: 399 case net::HTTP_RESET_CONTENT: 400 case net::HTTP_PARTIAL_CONTENT: 401 // Expected successful codes. 402 break; 403 case net::HTTP_NO_CONTENT: 404 case net::HTTP_NOT_FOUND: 405 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 406 break; 407 case net::HTTP_PRECONDITION_FAILED: 408 // Failed our 'If-Unmodified-Since' or 'If-Match'; see 409 // download_manager_impl.cc BeginDownload() 410 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION; 411 break; 412 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: 413 // Retry by downloading from the start automatically: 414 // If we haven't received data when we get this error, we won't. 415 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; 416 break; 417 case net::HTTP_UNAUTHORIZED: 418 // Server didn't authorize this request. 419 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; 420 break; 421 default: // All other errors. 422 // Redirection and informational codes should have been handled earlier 423 // in the stack. 424 DCHECK_NE(3, response_code / 100); 425 DCHECK_NE(1, response_code / 100); 426 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; 427 break; 428 } 429 } 430 431 std::string accept_ranges; 432 bool has_strong_validators = false; 433 if (request()->response_headers()) { 434 request()->response_headers()->EnumerateHeader( 435 NULL, "Accept-Ranges", &accept_ranges); 436 has_strong_validators = 437 request()->response_headers()->HasStrongValidators(); 438 } 439 RecordAcceptsRanges(accept_ranges, bytes_read_, has_strong_validators); 440 RecordNetworkBlockage(base::TimeTicks::Now() - download_start_time_, 441 total_pause_time_); 442 443 CallStartedCB(NULL, reason); 444 445 // Send the info down the stream. Conditional is in case we get 446 // OnResponseCompleted without OnResponseStarted. 447 if (stream_writer_) 448 stream_writer_->Close(reason); 449 450 // If the error mapped to something unknown, record it so that 451 // we can drill down. 452 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { 453 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Download.MapErrorNetworkFailed", 454 std::abs(status.error()), 455 net::GetAllErrorCodesForUma()); 456 } 457 458 stream_writer_.reset(); // We no longer need the stream. 459 read_buffer_ = NULL; 460} 461 462void DownloadResourceHandler::OnDataDownloaded(int bytes_downloaded) { 463 NOTREACHED(); 464} 465 466void DownloadResourceHandler::PauseRequest() { 467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 468 469 ++pause_count_; 470} 471 472void DownloadResourceHandler::ResumeRequest() { 473 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 474 DCHECK_LT(0, pause_count_); 475 476 --pause_count_; 477 478 if (!was_deferred_) 479 return; 480 if (pause_count_ > 0) 481 return; 482 483 was_deferred_ = false; 484 if (!last_stream_pause_time_.is_null()) { 485 total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); 486 last_stream_pause_time_ = base::TimeTicks(); 487 } 488 489 controller()->Resume(); 490} 491 492void DownloadResourceHandler::CancelRequest() { 493 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 494 495 const ResourceRequestInfo* info = GetRequestInfo(); 496 ResourceDispatcherHostImpl::Get()->CancelRequest( 497 info->GetChildID(), 498 info->GetRequestID()); 499 // This object has been deleted. 500} 501 502std::string DownloadResourceHandler::DebugString() const { 503 const ResourceRequestInfo* info = GetRequestInfo(); 504 return base::StringPrintf("{" 505 " url_ = " "\"%s\"" 506 " info = {" 507 " child_id = " "%d" 508 " request_id = " "%d" 509 " route_id = " "%d" 510 " }" 511 " }", 512 request() ? 513 request()->url().spec().c_str() : 514 "<NULL request>", 515 info->GetChildID(), 516 info->GetRequestID(), 517 info->GetRouteID()); 518} 519 520DownloadResourceHandler::~DownloadResourceHandler() { 521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 522 523 // This won't do anything if the callback was called before. 524 // If it goes through, it will likely be because OnWillStart() returned 525 // false somewhere in the chain of resource handlers. 526 CallStartedCB(NULL, DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); 527 528 // Remove output stream callback if a stream exists. 529 if (stream_writer_) 530 stream_writer_->RegisterCallback(base::Closure()); 531 532 // tab_info_ must be destroyed on UI thread, since 533 // InitializeDownloadTabInfoOnUIThread might still be using it. 534 if (tab_info_) 535 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, tab_info_); 536 537 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", 538 base::TimeTicks::Now() - download_start_time_); 539} 540 541} // namespace content 542