| Index: content/browser/renderer_host/redirect_to_file_resource_handler.cc
|
| ===================================================================
|
| --- content/browser/renderer_host/redirect_to_file_resource_handler.cc (revision 170763)
|
| +++ content/browser/renderer_host/redirect_to_file_resource_handler.cc (working copy)
|
| @@ -1,268 +0,0 @@
|
| -// Copyright (c) 2012 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 "content/browser/renderer_host/redirect_to_file_resource_handler.h"
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/file_util.h"
|
| -#include "base/file_util_proxy.h"
|
| -#include "base/logging.h"
|
| -#include "base/message_loop_proxy.h"
|
| -#include "base/platform_file.h"
|
| -#include "base/threading/thread_restrictions.h"
|
| -#include "content/browser/renderer_host/resource_dispatcher_host_impl.h"
|
| -#include "content/public/common/resource_response.h"
|
| -#include "net/base/file_stream.h"
|
| -#include "net/base/io_buffer.h"
|
| -#include "net/base/mime_sniffer.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "webkit/blob/shareable_file_reference.h"
|
| -
|
| -using webkit_blob::ShareableFileReference;
|
| -
|
| -namespace {
|
| -
|
| -// This class is similar to identically named classes in AsyncResourceHandler
|
| -// and BufferedResourceHandler, but not quite.
|
| -// TODO(ncbray) generalize and unify these cases?
|
| -// In general, it's a bad idea to point to a subbuffer (particularly with
|
| -// GrowableIOBuffer) because the backing IOBuffer may realloc its data. In this
|
| -// particular case we know RedirectToFileResourceHandler will not realloc its
|
| -// buffer while a write is occurring, so we should be safe. This property is
|
| -// somewhat fragile, however, and depending on it is dangerous. A more
|
| -// principled approach would require significant refactoring, however, so for
|
| -// the moment we're relying on fragile properties.
|
| -class DependentIOBuffer : public net::WrappedIOBuffer {
|
| - public:
|
| - DependentIOBuffer(net::IOBuffer* backing, char* memory)
|
| - : net::WrappedIOBuffer(memory),
|
| - backing_(backing) {
|
| - }
|
| - private:
|
| - virtual ~DependentIOBuffer() {}
|
| -
|
| - scoped_refptr<net::IOBuffer> backing_;
|
| -};
|
| -
|
| -}
|
| -
|
| -namespace content {
|
| -
|
| -static const int kInitialReadBufSize = 32768;
|
| -static const int kMaxReadBufSize = 524288;
|
| -
|
| -RedirectToFileResourceHandler::RedirectToFileResourceHandler(
|
| - scoped_ptr<ResourceHandler> next_handler,
|
| - int process_id,
|
| - ResourceDispatcherHostImpl* host)
|
| - : LayeredResourceHandler(next_handler.Pass()),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
|
| - host_(host),
|
| - process_id_(process_id),
|
| - request_id_(-1),
|
| - buf_(new net::GrowableIOBuffer()),
|
| - buf_write_pending_(false),
|
| - write_cursor_(0),
|
| - write_callback_pending_(false),
|
| - next_buffer_size_(kInitialReadBufSize),
|
| - did_defer_(false),
|
| - completed_during_write_(false) {
|
| -}
|
| -
|
| -RedirectToFileResourceHandler::~RedirectToFileResourceHandler() {
|
| -}
|
| -
|
| -bool RedirectToFileResourceHandler::OnResponseStarted(
|
| - int request_id,
|
| - ResourceResponse* response,
|
| - bool* defer) {
|
| - if (response->head.error_code == net::OK ||
|
| - response->head.error_code == net::ERR_IO_PENDING) {
|
| - DCHECK(deletable_file_ && !deletable_file_->path().empty());
|
| - response->head.download_file_path = deletable_file_->path();
|
| - }
|
| - return next_handler_->OnResponseStarted(request_id, response, defer);
|
| -}
|
| -
|
| -bool RedirectToFileResourceHandler::OnWillStart(int request_id,
|
| - const GURL& url,
|
| - bool* defer) {
|
| - request_id_ = request_id;
|
| - if (!deletable_file_) {
|
| - // Defer starting the request until we have created the temporary file.
|
| - // TODO(darin): This is sub-optimal. We should not delay starting the
|
| - // network request like this.
|
| - did_defer_ = *defer = true;
|
| - base::FileUtilProxy::CreateTemporary(
|
| - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
|
| - base::PLATFORM_FILE_ASYNC,
|
| - base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
|
| - weak_factory_.GetWeakPtr()));
|
| - return true;
|
| - }
|
| - return next_handler_->OnWillStart(request_id, url, defer);
|
| -}
|
| -
|
| -bool RedirectToFileResourceHandler::OnWillRead(int request_id,
|
| - net::IOBuffer** buf,
|
| - int* buf_size,
|
| - int min_size) {
|
| - DCHECK_EQ(-1, min_size);
|
| -
|
| - if (buf_->capacity() < next_buffer_size_)
|
| - buf_->SetCapacity(next_buffer_size_);
|
| -
|
| - // We should have paused this network request already if the buffer is full.
|
| - DCHECK(!BufIsFull());
|
| -
|
| - *buf = buf_;
|
| - *buf_size = buf_->RemainingCapacity();
|
| -
|
| - buf_write_pending_ = true;
|
| - return true;
|
| -}
|
| -
|
| -bool RedirectToFileResourceHandler::OnReadCompleted(int request_id,
|
| - int bytes_read,
|
| - bool* defer) {
|
| - DCHECK(buf_write_pending_);
|
| - buf_write_pending_ = false;
|
| -
|
| - // We use the buffer's offset field to record the end of the buffer.
|
| - int new_offset = buf_->offset() + bytes_read;
|
| - DCHECK(new_offset <= buf_->capacity());
|
| - buf_->set_offset(new_offset);
|
| -
|
| - if (BufIsFull()) {
|
| - did_defer_ = *defer = true;
|
| -
|
| - if (buf_->capacity() == bytes_read) {
|
| - // The network layer has saturated our buffer in one read. Next time, we
|
| - // should give it a bigger buffer for it to fill.
|
| - next_buffer_size_ = std::min(next_buffer_size_ * 2, kMaxReadBufSize);
|
| - }
|
| - }
|
| -
|
| - return WriteMore();
|
| -}
|
| -
|
| -bool RedirectToFileResourceHandler::OnResponseCompleted(
|
| - int request_id,
|
| - const net::URLRequestStatus& status,
|
| - const std::string& security_info) {
|
| - if (write_callback_pending_) {
|
| - completed_during_write_ = true;
|
| - completed_status_ = status;
|
| - completed_security_info_ = security_info;
|
| - return false;
|
| - }
|
| - return next_handler_->OnResponseCompleted(request_id, status, security_info);
|
| -}
|
| -
|
| -void RedirectToFileResourceHandler::DidCreateTemporaryFile(
|
| - base::PlatformFileError /*error_code*/,
|
| - base::PassPlatformFile file_handle,
|
| - const FilePath& file_path) {
|
| - deletable_file_ = ShareableFileReference::GetOrCreate(
|
| - file_path, ShareableFileReference::DELETE_ON_FINAL_RELEASE,
|
| - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
|
| - file_stream_.reset(new net::FileStream(file_handle.ReleaseValue(),
|
| - base::PLATFORM_FILE_WRITE |
|
| - base::PLATFORM_FILE_ASYNC,
|
| - NULL));
|
| - host_->RegisterDownloadedTempFile(
|
| - process_id_, request_id_, deletable_file_.get());
|
| - ResumeIfDeferred();
|
| -}
|
| -
|
| -void RedirectToFileResourceHandler::DidWriteToFile(int result) {
|
| - write_callback_pending_ = false;
|
| -
|
| - bool failed = false;
|
| - if (result > 0) {
|
| - next_handler_->OnDataDownloaded(request_id_, result);
|
| - write_cursor_ += result;
|
| - failed = !WriteMore();
|
| - } else {
|
| - failed = true;
|
| - }
|
| -
|
| - if (failed) {
|
| - ResumeIfDeferred();
|
| - } else if (completed_during_write_) {
|
| - if (next_handler_->OnResponseCompleted(request_id_, completed_status_,
|
| - completed_security_info_)) {
|
| - ResumeIfDeferred();
|
| - }
|
| - }
|
| -}
|
| -
|
| -bool RedirectToFileResourceHandler::WriteMore() {
|
| - DCHECK(file_stream_.get());
|
| - for (;;) {
|
| - if (write_cursor_ == buf_->offset()) {
|
| - // We've caught up to the network load, but it may be in the process of
|
| - // appending more data to the buffer.
|
| - if (!buf_write_pending_) {
|
| - if (BufIsFull())
|
| - ResumeIfDeferred();
|
| - buf_->set_offset(0);
|
| - write_cursor_ = 0;
|
| - }
|
| - return true;
|
| - }
|
| - if (write_callback_pending_)
|
| - return true;
|
| - DCHECK(write_cursor_ < buf_->offset());
|
| -
|
| - // Create a temporary buffer pointing to a subsection of the data buffer so
|
| - // that it can be passed to Write. This code makes some crazy scary
|
| - // assumptions about object lifetimes, thread sharing, and that buf_ will
|
| - // not realloc durring the write due to how the state machine in this class
|
| - // works.
|
| - // Note that buf_ is also shared with the code that writes data into the
|
| - // cache, so modifying it can cause some pretty subtle race conditions:
|
| - // https://code.google.com/p/chromium/issues/detail?id=152076
|
| - // We're using DependentIOBuffer instead of DrainableIOBuffer to dodge some
|
| - // of these issues, for the moment.
|
| - // TODO(ncbray) make this code less crazy scary.
|
| - // Also note that Write may increase the refcount of "wrapped" deep in the
|
| - // bowels of its implementation, the use of scoped_refptr here is not
|
| - // spurious.
|
| - scoped_refptr<DependentIOBuffer> wrapped = new DependentIOBuffer(
|
| - buf_, buf_->StartOfBuffer() + write_cursor_);
|
| - int write_len = buf_->offset() - write_cursor_;
|
| -
|
| - int rv = file_stream_->Write(
|
| - wrapped,
|
| - write_len,
|
| - base::Bind(&RedirectToFileResourceHandler::DidWriteToFile,
|
| - base::Unretained(this)));
|
| - if (rv == net::ERR_IO_PENDING) {
|
| - write_callback_pending_ = true;
|
| - return true;
|
| - }
|
| - if (rv <= 0)
|
| - return false;
|
| - next_handler_->OnDataDownloaded(request_id_, rv);
|
| - write_cursor_ += rv;
|
| - }
|
| -}
|
| -
|
| -bool RedirectToFileResourceHandler::BufIsFull() const {
|
| - // This is a hack to workaround BufferedResourceHandler's inability to
|
| - // deal with a ResourceHandler that returns a buffer size of less than
|
| - // 2 * net::kMaxBytesToSniff from its OnWillRead method.
|
| - // TODO(darin): Fix this retardation!
|
| - return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff);
|
| -}
|
| -
|
| -void RedirectToFileResourceHandler::ResumeIfDeferred() {
|
| - if (did_defer_) {
|
| - did_defer_ = false;
|
| - controller()->Resume();
|
| - }
|
| -}
|
| -
|
| -} // namespace content
|
|
|