diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-20 01:18:59 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-20 01:18:59 +0000 |
commit | fe54944f6080ed9f48619899ad342924754b19a9 (patch) | |
tree | 7959716d0543c6699cd254af29ba963b48fecd69 /chrome/browser/renderer_host/buffered_resource_handler.cc | |
parent | 6995f1dfc7d50b1296221b3875d79d31a978eb6a (diff) | |
download | chromium_src-fe54944f6080ed9f48619899ad342924754b19a9.zip chromium_src-fe54944f6080ed9f48619899ad342924754b19a9.tar.gz chromium_src-fe54944f6080ed9f48619899ad342924754b19a9.tar.bz2 |
This CL moves the different resource handlers from resource_dispatcher_host.cc into their own files, into a new renderer_host directory.
Their is no functionality change in this CL.
The motivation for doing that was:
- resource_dispatcher_host.cc has become very long and more than 1/3 of the code is for the resource handler.
- having the resource handler in their own file allows having unit tests for them.
BUG=none
TEST=run all unit tests and UI tests.
Review URL: http://codereview.chromium.org/14487
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7336 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/renderer_host/buffered_resource_handler.cc')
-rw-r--r-- | chrome/browser/renderer_host/buffered_resource_handler.cc | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/chrome/browser/renderer_host/buffered_resource_handler.cc b/chrome/browser/renderer_host/buffered_resource_handler.cc new file mode 100644 index 0000000..ae9fa35 --- /dev/null +++ b/chrome/browser/renderer_host/buffered_resource_handler.cc @@ -0,0 +1,231 @@ +// Copyright (c) 2006-2008 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/buffered_resource_handler.h" + +#include "net/base/mime_sniffer.h" +#include "chrome/browser/renderer_host/download_throttling_resource_handler.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" + +BufferedResourceHandler::BufferedResourceHandler(ResourceHandler* handler, + ResourceDispatcherHost* host, + URLRequest* request) + : real_handler_(handler), + host_(host), + request_(request), + bytes_read_(0), + sniff_content_(false), + should_buffer_(false), + buffering_(false), + finished_(false) { +} + +bool BufferedResourceHandler::OnUploadProgress(int request_id, + uint64 position, + uint64 size) { + return real_handler_->OnUploadProgress(request_id, position, size); +} + +bool BufferedResourceHandler::OnRequestRedirected(int request_id, + const GURL& new_url) { + return real_handler_->OnRequestRedirected(request_id, new_url); +} + +bool BufferedResourceHandler::OnResponseStarted(int request_id, + ResourceResponse* response) { + response_ = response; + if (!DelayResponse()) + return CompleteResponseStarted(request_id, false); + return true; +} + + +bool BufferedResourceHandler::OnResponseCompleted( + int request_id, const URLRequestStatus& status) { + return real_handler_->OnResponseCompleted(request_id, status); +} + +// We'll let the original event handler provide a buffer, and reuse it for +// subsequent reads until we're done buffering. +bool BufferedResourceHandler::OnWillRead(int request_id, + char** buf, int* buf_size, + int min_size) { + if (buffering_) { + *buf = read_buffer_ + bytes_read_; + *buf_size = read_buffer_size_ - bytes_read_; + DCHECK(*buf_size > 0); + return true; + } + + if (finished_) + return false; + + bool ret = real_handler_->OnWillRead(request_id, buf, buf_size, min_size); + read_buffer_ = *buf; + read_buffer_size_ = *buf_size; + bytes_read_ = 0; + return ret; +} + +bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { + ResourceDispatcherHost::ExtraRequestInfo* info = + ResourceDispatcherHost::ExtraInfoForRequest(request_); + + if (sniff_content_ || should_buffer_) { + if (KeepBuffering(*bytes_read)) + return true; + + LOG(INFO) << "Finished buffering " << request_->url().spec(); + sniff_content_ = should_buffer_ = false; + *bytes_read = bytes_read_; + + // Done buffering, send the pending ResponseStarted event. + if (!CompleteResponseStarted(request_id, true)) + return false; + } + + return real_handler_->OnReadCompleted(request_id, bytes_read); +} + +bool BufferedResourceHandler::DelayResponse() { + std::string mime_type; + request_->GetMimeType(&mime_type); + + std::string content_type_options; + request_->GetResponseHeaderByName("x-content-type-options", + &content_type_options); + if (content_type_options != "nosniff" && + net::ShouldSniffMimeType(request_->url(), mime_type)) { + // We're going to look at the data before deciding what the content type + // is. That means we need to delay sending the ResponseStarted message + // over the IPC channel. + sniff_content_ = true; + LOG(INFO) << "To buffer: " << request_->url().spec(); + return true; + } + + if (ShouldBuffer(request_->url(), mime_type)) { + // This is a temporary fix for the fact that webkit expects to have + // enough data to decode the doctype in order to select the rendering + // mode. + should_buffer_ = true; + LOG(INFO) << "To buffer: " << request_->url().spec(); + return true; + } + return false; +} + +bool BufferedResourceHandler::ShouldBuffer(const GURL& url, + const std::string& mime_type) { + // We are willing to buffer for HTTP and HTTPS. + bool sniffable_scheme = url.is_empty() || + url.SchemeIs("http") || + url.SchemeIs("https"); + if (!sniffable_scheme) + return false; + + // Today, the only reason to buffer the request is to fix the doctype decoding + // performed by webkit: if there is not enough data it will go to quirks mode. + // We only expect the doctype check to apply to html documents. + return mime_type == "text/html"; +} + +bool BufferedResourceHandler::KeepBuffering(int bytes_read) { + DCHECK(read_buffer_); + bytes_read_ += bytes_read; + finished_ = (bytes_read == 0); + + if (sniff_content_) { + std::string type_hint, new_type; + request_->GetMimeType(&type_hint); + + if (!net::SniffMimeType(read_buffer_, bytes_read_, request_->url(), + type_hint, &new_type)) { + // SniffMimeType() returns false if there is not enough data to determine + // the mime type. However, even if it returns false, it returns a new type + // that is probably better than the current one. + DCHECK(bytes_read_ < 512 /*kMaxBytesToSniff*/); + if (!finished_) { + buffering_ = true; + return true; + } + } + sniff_content_ = false; + response_->response_head.mime_type.assign(new_type); + + // We just sniffed the mime type, maybe there is a doctype to process. + if (ShouldBuffer(request_->url(), new_type)) + should_buffer_ = true; + } + + if (!finished_ && should_buffer_) { + if (!DidBufferEnough(bytes_read_)) { + buffering_ = true; + return true; + } + } + buffering_ = false; + return false; +} + +bool BufferedResourceHandler::CompleteResponseStarted(int request_id, + bool in_complete) { + // Check to see if we should forward the data from this request to the + // download thread. + // TODO(paulg): Only download if the context from the renderer allows it. + std::string content_disposition; + request_->GetResponseHeaderByName("content-disposition", + &content_disposition); + + ResourceDispatcherHost::ExtraRequestInfo* info = + ResourceDispatcherHost::ExtraInfoForRequest(request_); + + if (info->allow_download && + host_->ShouldDownload(response_->response_head.mime_type, + content_disposition)) { + if (response_->response_head.headers && // Can be NULL if FTP. + response_->response_head.headers->response_code() / 100 != 2) { + // The response code indicates that this is an error page, but we don't + // know how to display the content. We follow Firefox here and show our + // own error page instead of triggering a download. + // TODO(abarth): We should abstract the response_code test, but this kind + // of check is scattered throughout our codebase. + request_->CancelWithError(net::ERR_FILE_NOT_FOUND); + return false; + } + + info->is_download = true; + + scoped_refptr<DownloadThrottlingResourceHandler> download_handler = + new DownloadThrottlingResourceHandler(host_, + request_, + request_->url().spec(), + info->render_process_host_id, + info->render_view_id, + request_id, + in_complete); + if (bytes_read_) { + // a Read has already occurred and we need to copy the data into the + // EventHandler. + char *buf = NULL; + int buf_len = 0; + download_handler->OnWillRead(request_id, &buf, &buf_len, bytes_read_); + CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0)); + memcpy(buf, read_buffer_, bytes_read_); + } + // Update the renderer with the response headers which will cause it to + // cancel the request. + // TODO(paulg): Send the renderer a response that indicates that the request + // will be handled by an external source (the browser). + real_handler_->OnResponseStarted(info->request_id, response_); + real_handler_ = download_handler; + } + return real_handler_->OnResponseStarted(request_id, response_); +} + +bool BufferedResourceHandler::DidBufferEnough(int bytes_read) { + const int kRequiredLength = 256; + + return bytes_read >= kRequiredLength; +} |