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