// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/renderer_host/download_throttling_resource_handler.h" #include "base/logging.h" #include "chrome/browser/download/download_util.h" #include "chrome/browser/renderer_host/download_resource_handler.h" #include "content/browser/renderer_host/resource_dispatcher_host.h" #include "content/common/resource_response.h" #include "net/base/io_buffer.h" #include "net/base/mime_sniffer.h" DownloadThrottlingResourceHandler::DownloadThrottlingResourceHandler( ResourceDispatcherHost* host, net::URLRequest* request, const GURL& url, int render_process_host_id, int render_view_id, int request_id, bool in_complete) : host_(host), request_(request), url_(url), render_process_host_id_(render_process_host_id), render_view_id_(render_view_id), request_id_(request_id), tmp_buffer_length_(0), ignore_on_read_complete_(in_complete), request_closed_(false) { download_util::RecordDownloadCount( download_util::INITIATED_BY_NAVIGATION_COUNT); // Pause the request. host_->PauseRequest(render_process_host_id_, request_id_, true); // Add a reference to ourselves to keep this object alive until we // receive a callback from DownloadRequestLimiter. The reference is // released in ContinueDownload() and CancelDownload(). AddRef(); host_->download_request_limiter()->CanDownloadOnIOThread( render_process_host_id_, render_view_id, request_id, this); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableFunction(&download_util::NotifyDownloadInitiated, render_process_host_id_, render_view_id_)); } DownloadThrottlingResourceHandler::~DownloadThrottlingResourceHandler() { } bool DownloadThrottlingResourceHandler::OnUploadProgress(int request_id, uint64 position, uint64 size) { DCHECK(!request_closed_); if (download_handler_.get()) return download_handler_->OnUploadProgress(request_id, position, size); return true; } bool DownloadThrottlingResourceHandler::OnRequestRedirected( int request_id, const GURL& url, ResourceResponse* response, bool* defer) { DCHECK(!request_closed_); if (download_handler_.get()) { return download_handler_->OnRequestRedirected( request_id, url, response, defer); } url_ = url; return true; } bool DownloadThrottlingResourceHandler::OnResponseStarted( int request_id, ResourceResponse* response) { DCHECK(!request_closed_); if (download_handler_.get()) return download_handler_->OnResponseStarted(request_id, response); response_ = response; return true; } bool DownloadThrottlingResourceHandler::OnWillStart(int request_id, const GURL& url, bool* defer) { DCHECK(!request_closed_); if (download_handler_.get()) return download_handler_->OnWillStart(request_id, url, defer); return true; } bool DownloadThrottlingResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, int min_size) { DCHECK(!request_closed_); if (download_handler_.get()) return download_handler_->OnWillRead(request_id, buf, buf_size, min_size); // We should only have this invoked once, as such we only deal with one // tmp buffer. DCHECK(!tmp_buffer_.get()); // If the caller passed a negative |min_size| then chose an appropriate // default. The BufferedResourceHandler requires this to be at least 2 times // the size required for mime detection. if (min_size < 0) min_size = 2 * net::kMaxBytesToSniff; tmp_buffer_ = new net::IOBuffer(min_size); *buf = tmp_buffer_.get(); *buf_size = min_size; return true; } bool DownloadThrottlingResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { DCHECK(!request_closed_); if (ignore_on_read_complete_) { // See comments above definition for details on this. ignore_on_read_complete_ = false; return true; } if (!*bytes_read) return true; if (tmp_buffer_.get()) { DCHECK(!tmp_buffer_length_); tmp_buffer_length_ = *bytes_read; if (download_handler_.get()) CopyTmpBufferToDownloadHandler(); return true; } if (download_handler_.get()) return download_handler_->OnReadCompleted(request_id, bytes_read); return true; } bool DownloadThrottlingResourceHandler::OnResponseCompleted( int request_id, const net::URLRequestStatus& status, const std::string& security_info) { DCHECK(!request_closed_); if (download_handler_.get()) return download_handler_->OnResponseCompleted(request_id, status, security_info); // For a download, if ResourceDispatcher::Read() fails, // ResourceDispatcher::OnresponseStarted() will call // OnResponseCompleted(), and we will end up here with an error // status. if (!status.is_success()) return false; NOTREACHED(); return true; } void DownloadThrottlingResourceHandler::OnRequestClosed() { DCHECK(!request_closed_); if (download_handler_.get()) download_handler_->OnRequestClosed(); request_closed_ = true; } void DownloadThrottlingResourceHandler::CancelDownload() { if (!request_closed_) host_->CancelRequest(render_process_host_id_, request_id_, false); Release(); // Release the additional reference from constructor. } void DownloadThrottlingResourceHandler::ContinueDownload() { DCHECK(!download_handler_.get()); if (!request_closed_) { download_handler_ = new DownloadResourceHandler(host_, render_process_host_id_, render_view_id_, request_id_, url_, host_->download_file_manager(), request_, false, DownloadSaveInfo()); if (response_.get()) download_handler_->OnResponseStarted(request_id_, response_.get()); if (tmp_buffer_length_) CopyTmpBufferToDownloadHandler(); // And let the request continue. host_->PauseRequest(render_process_host_id_, request_id_, false); } Release(); // Release the addtional reference from constructor. } void DownloadThrottlingResourceHandler::CopyTmpBufferToDownloadHandler() { // Copy over the tmp buffer. net::IOBuffer* buffer; int buf_size; if (download_handler_->OnWillRead(request_id_, &buffer, &buf_size, tmp_buffer_length_)) { CHECK(buf_size >= tmp_buffer_length_); memcpy(buffer->data(), tmp_buffer_->data(), tmp_buffer_length_); download_handler_->OnReadCompleted(request_id_, &tmp_buffer_length_); } tmp_buffer_length_ = 0; tmp_buffer_ = NULL; }