summaryrefslogtreecommitdiffstats
path: root/chrome/browser/renderer_host/buffered_resource_handler.cc
diff options
context:
space:
mode:
authorjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-12-20 01:18:59 +0000
committerjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-12-20 01:18:59 +0000
commitfe54944f6080ed9f48619899ad342924754b19a9 (patch)
tree7959716d0543c6699cd254af29ba963b48fecd69 /chrome/browser/renderer_host/buffered_resource_handler.cc
parent6995f1dfc7d50b1296221b3875d79d31a978eb6a (diff)
downloadchromium_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.cc231
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;
+}