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 37 namespace content { 38 39 struct DownloadResourceHandler::DownloadTabInfo { 40 GURL tab_url; 41 GURL tab_referrer_url; 42 }; 43 44 namespace { 45 46 void 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. 59 static 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 85 void 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 102 const int DownloadResourceHandler::kDownloadByteStreamSize = 100 * 1024; 103 104 DownloadResourceHandler::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 138 bool DownloadResourceHandler::OnUploadProgress(uint64 position, 139 uint64 size) { 140 return true; 141 } 142 143 bool DownloadResourceHandler::OnRequestRedirected( 144 const GURL& url, 145 ResourceResponse* response, 146 bool* defer) { 147 // We treat a download as a main frame load, and thus update the policy URL 148 // on redirects. 149 request()->set_first_party_for_cookies(url); 150 return true; 151 } 152 153 // Send the download creation information to the download thread. 154 bool DownloadResourceHandler::OnResponseStarted( 155 ResourceResponse* response, 156 bool* defer) { 157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 158 // There can be only one (call) 159 DCHECK(!on_response_started_called_); 160 on_response_started_called_ = true; 161 162 VLOG(20) << __FUNCTION__ << "()" << DebugString(); 163 download_start_time_ = base::TimeTicks::Now(); 164 165 // If it's a download, we don't want to poison the cache with it. 166 request()->StopCaching(); 167 168 // Lower priority as well, so downloads don't contend for resources 169 // with main frames. 170 request()->SetPriority(net::IDLE); 171 172 // If the content-length header is not present (or contains something other 173 // than numbers), the incoming content_length is -1 (unknown size). 174 // Set the content length to 0 to indicate unknown size to DownloadManager. 175 int64 content_length = 176 response->head.content_length > 0 ? response->head.content_length : 0; 177 178 const ResourceRequestInfoImpl* request_info = GetRequestInfo(); 179 180 // Deleted in DownloadManager. 181 scoped_ptr<DownloadCreateInfo> info( 182 new DownloadCreateInfo(base::Time::Now(), 183 content_length, 184 request()->net_log(), 185 request_info->HasUserGesture(), 186 request_info->GetPageTransition(), 187 save_info_.Pass())); 188 189 // Create the ByteStream for sending data to the download sink. 190 scoped_ptr<ByteStreamReader> stream_reader; 191 CreateByteStream( 192 base::MessageLoopProxy::current(), 193 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), 194 kDownloadByteStreamSize, &stream_writer_, &stream_reader); 195 stream_writer_->RegisterCallback( 196 base::Bind(&DownloadResourceHandler::ResumeRequest, AsWeakPtr())); 197 198 info->download_id = download_id_; 199 info->url_chain = request()->url_chain(); 200 info->referrer_url = GURL(request()->referrer()); 201 info->mime_type = response->head.mime_type; 202 info->remote_address = request()->GetSocketAddress().host(); 203 request()->GetResponseHeaderByName("content-disposition", 204 &info->content_disposition); 205 RecordDownloadMimeType(info->mime_type); 206 RecordDownloadContentDisposition(info->content_disposition); 207 208 info->request_handle = 209 DownloadRequestHandle(AsWeakPtr(), request_info->GetChildID(), 210 request_info->GetRouteID(), 211 request_info->GetRequestID()); 212 213 // Get the last modified time and etag. 214 const net::HttpResponseHeaders* headers = request()->response_headers(); 215 if (headers) { 216 if (headers->HasStrongValidators()) { 217 // If we don't have strong validators as per RFC 2616 section 13.3.3, then 218 // we neither store nor use them for range requests. 219 if (!headers->EnumerateHeader(NULL, "Last-Modified", 220 &info->last_modified)) 221 info->last_modified.clear(); 222 if (!headers->EnumerateHeader(NULL, "ETag", &info->etag)) 223 info->etag.clear(); 224 } 225 226 int status = headers->response_code(); 227 if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { 228 // Success & not range response; if we asked for a range, we didn't 229 // get it--reset the file pointers to reflect that. 230 info->save_info->offset = 0; 231 info->save_info->hash_state = ""; 232 } 233 234 if (!headers->GetMimeType(&info->original_mime_type)) 235 info->original_mime_type.clear(); 236 } 237 238 // Blink verifies that the requester of this download is allowed to set a 239 // suggested name for the security origin of the downlaod URL. However, this 240 // assumption doesn't hold if there were cross origin redirects. Therefore, 241 // clear the suggested_name for such requests. 242 if (info->url_chain.size() > 1 && 243 info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) 244 info->save_info->suggested_name.clear(); 245 246 BrowserThread::PostTask( 247 BrowserThread::UI, FROM_HERE, 248 base::Bind(&StartOnUIThread, 249 base::Passed(&info), 250 base::Owned(tab_info_), 251 base::Passed(&stream_reader), 252 // Pass to StartOnUIThread so that variable 253 // access is always on IO thread but function 254 // is called on UI thread. 255 started_cb_)); 256 // Now owned by the task that was just posted. 257 tab_info_ = NULL; 258 // Guaranteed to be called in StartOnUIThread 259 started_cb_.Reset(); 260 261 return true; 262 } 263 264 void DownloadResourceHandler::CallStartedCB( 265 DownloadItem* item, 266 DownloadInterruptReason interrupt_reason) { 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 268 if (started_cb_.is_null()) 269 return; 270 BrowserThread::PostTask( 271 BrowserThread::UI, 272 FROM_HERE, 273 base::Bind( 274 &CallStartedCBOnUIThread, started_cb_, item, interrupt_reason)); 275 started_cb_.Reset(); 276 } 277 278 bool DownloadResourceHandler::OnWillStart(const GURL& url, bool* defer) { 279 return true; 280 } 281 282 bool DownloadResourceHandler::OnBeforeNetworkStart(const GURL& url, 283 bool* defer) { 284 return true; 285 } 286 287 // Create a new buffer, which will be handed to the download thread for file 288 // writing and deletion. 289 bool DownloadResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, 290 int* buf_size, 291 int min_size) { 292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 293 DCHECK(buf && buf_size); 294 DCHECK(!read_buffer_.get()); 295 296 *buf_size = min_size < 0 ? kReadBufSize : min_size; 297 last_buffer_size_ = *buf_size; 298 read_buffer_ = new net::IOBuffer(*buf_size); 299 *buf = read_buffer_.get(); 300 return true; 301 } 302 303 // Pass the buffer to the download file writer. 304 bool DownloadResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { 305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 306 DCHECK(read_buffer_.get()); 307 308 base::TimeTicks now(base::TimeTicks::Now()); 309 if (!last_read_time_.is_null()) { 310 double seconds_since_last_read = (now - last_read_time_).InSecondsF(); 311 if (now == last_read_time_) 312 // Use 1/10 ms as a "very small number" so that we avoid 313 // divide-by-zero error and still record a very high potential bandwidth. 314 seconds_since_last_read = 0.00001; 315 316 double actual_bandwidth = (bytes_read)/seconds_since_last_read; 317 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; 318 RecordBandwidth(actual_bandwidth, potential_bandwidth); 319 } 320 last_read_time_ = now; 321 322 if (!bytes_read) 323 return true; 324 bytes_read_ += bytes_read; 325 DCHECK(read_buffer_.get()); 326 327 // Take the data ship it down the stream. If the stream is full, pause the 328 // request; the stream callback will resume it. 329 if (!stream_writer_->Write(read_buffer_, bytes_read)) { 330 PauseRequest(); 331 *defer = was_deferred_ = true; 332 last_stream_pause_time_ = now; 333 } 334 335 read_buffer_ = NULL; // Drop our reference. 336 337 if (pause_count_ > 0) 338 *defer = was_deferred_ = true; 339 340 return true; 341 } 342 343 void DownloadResourceHandler::OnResponseCompleted( 344 const net::URLRequestStatus& status, 345 const std::string& security_info, 346 bool* defer) { 347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 348 int response_code = status.is_success() ? request()->GetResponseCode() : 0; 349 VLOG(20) << __FUNCTION__ << "()" << DebugString() 350 << " status.status() = " << status.status() 351 << " status.error() = " << status.error() 352 << " response_code = " << response_code; 353 354 net::Error error_code = net::OK; 355 if (status.status() == net::URLRequestStatus::FAILED || 356 // Note cancels as failures too. 357 status.status() == net::URLRequestStatus::CANCELED) { 358 error_code = static_cast<net::Error>(status.error()); // Normal case. 359 // Make sure that at least the fact of failure comes through. 360 if (error_code == net::OK) 361 error_code = net::ERR_FAILED; 362 } 363 364 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are 365 // allowed since a number of servers in the wild close the connection too 366 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - 367 // treat downloads as complete in both cases, so we follow their lead. 368 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || 369 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { 370 error_code = net::OK; 371 } 372 DownloadInterruptReason reason = 373 ConvertNetErrorToInterruptReason( 374 error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); 375 376 if (status.status() == net::URLRequestStatus::CANCELED && 377 status.error() == net::ERR_ABORTED) { 378 // CANCELED + ERR_ABORTED == something outside of the network 379 // stack cancelled the request. There aren't that many things that 380 // could do this to a download request (whose lifetime is separated from 381 // the tab from which it came). We map this to USER_CANCELLED as the 382 // case we know about (system suspend because of laptop close) corresponds 383 // to a user action. 384 // TODO(ahendrickson) -- Find a better set of codes to use here, as 385 // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. 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 default: // All other errors. 418 // Redirection and informational codes should have been handled earlier 419 // in the stack. 420 DCHECK_NE(3, response_code / 100); 421 DCHECK_NE(1, response_code / 100); 422 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; 423 break; 424 } 425 } 426 427 std::string accept_ranges; 428 bool has_strong_validators = false; 429 if (request()->response_headers()) { 430 request()->response_headers()->EnumerateHeader( 431 NULL, "Accept-Ranges", &accept_ranges); 432 has_strong_validators = 433 request()->response_headers()->HasStrongValidators(); 434 } 435 RecordAcceptsRanges(accept_ranges, bytes_read_, has_strong_validators); 436 RecordNetworkBlockage(base::TimeTicks::Now() - download_start_time_, 437 total_pause_time_); 438 439 CallStartedCB(NULL, reason); 440 441 // Send the info down the stream. Conditional is in case we get 442 // OnResponseCompleted without OnResponseStarted. 443 if (stream_writer_) 444 stream_writer_->Close(reason); 445 446 // If the error mapped to something unknown, record it so that 447 // we can drill down. 448 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { 449 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Download.MapErrorNetworkFailed", 450 std::abs(status.error()), 451 net::GetAllErrorCodesForUma()); 452 } 453 454 stream_writer_.reset(); // We no longer need the stream. 455 read_buffer_ = NULL; 456 } 457 458 void DownloadResourceHandler::OnDataDownloaded(int bytes_downloaded) { 459 NOTREACHED(); 460 } 461 462 void DownloadResourceHandler::PauseRequest() { 463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 464 465 ++pause_count_; 466 } 467 468 void DownloadResourceHandler::ResumeRequest() { 469 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 470 DCHECK_LT(0, pause_count_); 471 472 --pause_count_; 473 474 if (!was_deferred_) 475 return; 476 if (pause_count_ > 0) 477 return; 478 479 was_deferred_ = false; 480 if (!last_stream_pause_time_.is_null()) { 481 total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); 482 last_stream_pause_time_ = base::TimeTicks(); 483 } 484 485 controller()->Resume(); 486 } 487 488 void DownloadResourceHandler::CancelRequest() { 489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 490 491 const ResourceRequestInfo* info = GetRequestInfo(); 492 ResourceDispatcherHostImpl::Get()->CancelRequest( 493 info->GetChildID(), 494 info->GetRequestID()); 495 // This object has been deleted. 496 } 497 498 std::string DownloadResourceHandler::DebugString() const { 499 const ResourceRequestInfo* info = GetRequestInfo(); 500 return base::StringPrintf("{" 501 " url_ = " "\"%s\"" 502 " info = {" 503 " child_id = " "%d" 504 " request_id = " "%d" 505 " route_id = " "%d" 506 " }" 507 " }", 508 request() ? 509 request()->url().spec().c_str() : 510 "<NULL request>", 511 info->GetChildID(), 512 info->GetRequestID(), 513 info->GetRouteID()); 514 } 515 516 DownloadResourceHandler::~DownloadResourceHandler() { 517 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 518 519 // This won't do anything if the callback was called before. 520 // If it goes through, it will likely be because OnWillStart() returned 521 // false somewhere in the chain of resource handlers. 522 CallStartedCB(NULL, DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); 523 524 // Remove output stream callback if a stream exists. 525 if (stream_writer_) 526 stream_writer_->RegisterCallback(base::Closure()); 527 528 // tab_info_ must be destroyed on UI thread, since 529 // InitializeDownloadTabInfoOnUIThread might still be using it. 530 if (tab_info_) 531 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, tab_info_); 532 533 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", 534 base::TimeTicks::Now() - download_start_time_); 535 } 536 537 } // namespace content 538