blob: 2903c89fdcb57176b59eec207e822a268dc163a6 [file] [log] [blame]
[email protected]e3c404b2008-12-23 01:07:321// Copyright (c) 2006-2008 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 "chrome/browser/renderer_host/buffered_resource_handler.h"
6
[email protected]306291392009-01-17 19:15:367#include "base/histogram.h"
[email protected]5203e6002009-07-29 03:42:008#include "base/logging.h"
[email protected]319d9e6f2009-02-18 19:47:219#include "base/string_util.h"
[email protected]e3c404b2008-12-23 01:07:3210#include "net/base/mime_sniffer.h"
[email protected]597cf6e2009-05-29 09:43:2611#include "net/base/net_errors.h"
[email protected]f7a015e2009-08-15 02:50:1912#include "chrome/browser/browser_process.h"
[email protected]35fa6a22009-08-15 00:04:0113#include "chrome/browser/chrome_thread.h"
[email protected]e3c404b2008-12-23 01:07:3214#include "chrome/browser/renderer_host/download_throttling_resource_handler.h"
15#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
[email protected]dcf7d352009-02-26 01:56:0216#include "chrome/common/url_constants.h"
[email protected]9dea9e1f2009-01-29 00:30:4717#include "net/base/mime_sniffer.h"
[email protected]35fa6a22009-08-15 00:04:0118#include "net/base/mime_util.h"
[email protected]9dea9e1f2009-01-29 00:30:4719#include "net/base/io_buffer.h"
[email protected]319d9e6f2009-02-18 19:47:2120#include "net/http/http_response_headers.h"
[email protected]35fa6a22009-08-15 00:04:0121#include "webkit/glue/plugins/plugin_list.h"
[email protected]e3c404b2008-12-23 01:07:3222
[email protected]306291392009-01-17 19:15:3623namespace {
24
[email protected]9dea9e1f2009-01-29 00:30:4725const int kMaxBytesToSniff = 512;
26
[email protected]306291392009-01-17 19:15:3627void RecordSnifferMetrics(bool sniffing_blocked,
28 bool we_would_like_to_sniff,
29 const std::string& mime_type) {
[email protected]553dba62009-02-24 19:08:2330 static BooleanHistogram nosniff_usage("nosniff.usage");
[email protected]306291392009-01-17 19:15:3631 nosniff_usage.SetFlags(kUmaTargetedHistogramFlag);
32 nosniff_usage.AddBoolean(sniffing_blocked);
33
34 if (sniffing_blocked) {
[email protected]553dba62009-02-24 19:08:2335 static BooleanHistogram nosniff_otherwise("nosniff.otherwise");
[email protected]306291392009-01-17 19:15:3636 nosniff_otherwise.SetFlags(kUmaTargetedHistogramFlag);
37 nosniff_otherwise.AddBoolean(we_would_like_to_sniff);
38
[email protected]553dba62009-02-24 19:08:2339 static BooleanHistogram nosniff_empty_mime_type("nosniff.empty_mime_type");
[email protected]306291392009-01-17 19:15:3640 nosniff_empty_mime_type.SetFlags(kUmaTargetedHistogramFlag);
41 nosniff_empty_mime_type.AddBoolean(mime_type.empty());
42 }
43}
44
45} // namespace
46
[email protected]e3c404b2008-12-23 01:07:3247BufferedResourceHandler::BufferedResourceHandler(ResourceHandler* handler,
48 ResourceDispatcherHost* host,
49 URLRequest* request)
50 : real_handler_(handler),
51 host_(host),
52 request_(request),
53 bytes_read_(0),
54 sniff_content_(false),
55 should_buffer_(false),
[email protected]35fa6a22009-08-15 00:04:0156 wait_for_plugins_(false),
[email protected]e3c404b2008-12-23 01:07:3257 buffering_(false),
58 finished_(false) {
59}
60
61bool BufferedResourceHandler::OnUploadProgress(int request_id,
62 uint64 position,
63 uint64 size) {
64 return real_handler_->OnUploadProgress(request_id, position, size);
65}
66
67bool BufferedResourceHandler::OnRequestRedirected(int request_id,
[email protected]6568a9e32009-07-30 18:01:3968 const GURL& new_url,
69 ResourceResponse* response,
70 bool* defer) {
71 return real_handler_->OnRequestRedirected(
72 request_id, new_url, response, defer);
[email protected]e3c404b2008-12-23 01:07:3273}
74
75bool BufferedResourceHandler::OnResponseStarted(int request_id,
76 ResourceResponse* response) {
77 response_ = response;
78 if (!DelayResponse())
79 return CompleteResponseStarted(request_id, false);
80 return true;
81}
82
[email protected]e3c404b2008-12-23 01:07:3283bool BufferedResourceHandler::OnResponseCompleted(
[email protected]c4891b32009-03-08 07:41:3184 int request_id,
85 const URLRequestStatus& status,
86 const std::string& security_info) {
87 return real_handler_->OnResponseCompleted(request_id, status, security_info);
[email protected]e3c404b2008-12-23 01:07:3288}
89
[email protected]35fa6a22009-08-15 00:04:0190void BufferedResourceHandler::OnRequestClosed() {
91 request_ = NULL;
92 real_handler_->OnRequestClosed();
93}
94
[email protected]e3c404b2008-12-23 01:07:3295// We'll let the original event handler provide a buffer, and reuse it for
96// subsequent reads until we're done buffering.
[email protected]9dea9e1f2009-01-29 00:30:4797bool BufferedResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
98 int* buf_size, int min_size) {
[email protected]e3c404b2008-12-23 01:07:3299 if (buffering_) {
[email protected]9dea9e1f2009-01-29 00:30:47100 DCHECK(!my_buffer_.get());
101 my_buffer_ = new net::IOBuffer(kMaxBytesToSniff);
102 *buf = my_buffer_.get();
103 *buf_size = kMaxBytesToSniff;
[email protected]5203e6002009-07-29 03:42:00104 // TODO(willchan): Remove after debugging bug 16371.
105 CHECK((*buf)->data());
[email protected]e3c404b2008-12-23 01:07:32106 return true;
107 }
108
109 if (finished_)
110 return false;
111
[email protected]67199052009-06-12 21:15:33112 if (!real_handler_->OnWillRead(request_id, buf, buf_size, min_size)) {
113 return false;
114 }
[email protected]e3c404b2008-12-23 01:07:32115 read_buffer_ = *buf;
[email protected]5203e6002009-07-29 03:42:00116 // TODO(willchan): Remove after debugging bug 16371.
117 CHECK(read_buffer_->data());
[email protected]e3c404b2008-12-23 01:07:32118 read_buffer_size_ = *buf_size;
[email protected]f0a51fb52009-03-05 12:46:38119 DCHECK(read_buffer_size_ >= kMaxBytesToSniff * 2);
[email protected]e3c404b2008-12-23 01:07:32120 bytes_read_ = 0;
[email protected]67199052009-06-12 21:15:33121 return true;
[email protected]e3c404b2008-12-23 01:07:32122}
123
124bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
[email protected]e3c404b2008-12-23 01:07:32125 if (sniff_content_ || should_buffer_) {
126 if (KeepBuffering(*bytes_read))
127 return true;
128
129 LOG(INFO) << "Finished buffering " << request_->url().spec();
[email protected]e3c404b2008-12-23 01:07:32130 *bytes_read = bytes_read_;
131
132 // Done buffering, send the pending ResponseStarted event.
133 if (!CompleteResponseStarted(request_id, true))
134 return false;
[email protected]35fa6a22009-08-15 00:04:01135 } else if (wait_for_plugins_) {
136 return true;
[email protected]e3c404b2008-12-23 01:07:32137 }
138
[email protected]9dea9e1f2009-01-29 00:30:47139 // Release the reference that we acquired at OnWillRead.
140 read_buffer_ = NULL;
[email protected]e3c404b2008-12-23 01:07:32141 return real_handler_->OnReadCompleted(request_id, bytes_read);
142}
143
144bool BufferedResourceHandler::DelayResponse() {
145 std::string mime_type;
146 request_->GetMimeType(&mime_type);
147
148 std::string content_type_options;
149 request_->GetResponseHeaderByName("x-content-type-options",
150 &content_type_options);
[email protected]306291392009-01-17 19:15:36151
[email protected]423bd5b842009-01-23 17:30:50152 const bool sniffing_blocked =
153 LowerCaseEqualsASCII(content_type_options, "nosniff");
[email protected]306291392009-01-17 19:15:36154 const bool we_would_like_to_sniff =
155 net::ShouldSniffMimeType(request_->url(), mime_type);
156
157 RecordSnifferMetrics(sniffing_blocked, we_would_like_to_sniff, mime_type);
158
159 if (!sniffing_blocked && we_would_like_to_sniff) {
[email protected]e3c404b2008-12-23 01:07:32160 // We're going to look at the data before deciding what the content type
161 // is. That means we need to delay sending the ResponseStarted message
162 // over the IPC channel.
163 sniff_content_ = true;
164 LOG(INFO) << "To buffer: " << request_->url().spec();
165 return true;
166 }
167
[email protected]423bd5b842009-01-23 17:30:50168 if (sniffing_blocked && mime_type.empty()) {
169 // Ugg. The server told us not to sniff the content but didn't give us a
170 // mime type. What's a browser to do? Turns out, we're supposed to treat
171 // the response as "text/plain". This is the most secure option.
172 mime_type.assign("text/plain");
173 response_->response_head.mime_type.assign(mime_type);
174 }
175
[email protected]e3c404b2008-12-23 01:07:32176 if (ShouldBuffer(request_->url(), mime_type)) {
177 // This is a temporary fix for the fact that webkit expects to have
178 // enough data to decode the doctype in order to select the rendering
179 // mode.
180 should_buffer_ = true;
181 LOG(INFO) << "To buffer: " << request_->url().spec();
182 return true;
183 }
[email protected]35fa6a22009-08-15 00:04:01184
185 if (ShouldWaitForPlugins()) {
186 wait_for_plugins_ = true;
187 return true;
188 }
189
[email protected]e3c404b2008-12-23 01:07:32190 return false;
191}
192
193bool BufferedResourceHandler::ShouldBuffer(const GURL& url,
194 const std::string& mime_type) {
195 // We are willing to buffer for HTTP and HTTPS.
196 bool sniffable_scheme = url.is_empty() ||
[email protected]dcf7d352009-02-26 01:56:02197 url.SchemeIs(chrome::kHttpScheme) ||
198 url.SchemeIs(chrome::kHttpsScheme);
[email protected]e3c404b2008-12-23 01:07:32199 if (!sniffable_scheme)
200 return false;
201
202 // Today, the only reason to buffer the request is to fix the doctype decoding
203 // performed by webkit: if there is not enough data it will go to quirks mode.
204 // We only expect the doctype check to apply to html documents.
205 return mime_type == "text/html";
206}
207
[email protected]35fa6a22009-08-15 00:04:01208bool BufferedResourceHandler::DidBufferEnough(int bytes_read) {
209 const int kRequiredLength = 256;
210
211 return bytes_read >= kRequiredLength;
212}
213
[email protected]e3c404b2008-12-23 01:07:32214bool BufferedResourceHandler::KeepBuffering(int bytes_read) {
215 DCHECK(read_buffer_);
[email protected]9dea9e1f2009-01-29 00:30:47216 if (my_buffer_) {
217 // We are using our own buffer to read, update the main buffer.
218 CHECK(bytes_read + bytes_read_ < read_buffer_size_);
219 memcpy(read_buffer_->data() + bytes_read_, my_buffer_->data(), bytes_read);
220 my_buffer_ = NULL;
221 }
[email protected]e3c404b2008-12-23 01:07:32222 bytes_read_ += bytes_read;
223 finished_ = (bytes_read == 0);
224
225 if (sniff_content_) {
226 std::string type_hint, new_type;
227 request_->GetMimeType(&type_hint);
228
[email protected]9dea9e1f2009-01-29 00:30:47229 if (!net::SniffMimeType(read_buffer_->data(), bytes_read_,
230 request_->url(), type_hint, &new_type)) {
[email protected]e3c404b2008-12-23 01:07:32231 // SniffMimeType() returns false if there is not enough data to determine
232 // the mime type. However, even if it returns false, it returns a new type
233 // that is probably better than the current one.
[email protected]9dea9e1f2009-01-29 00:30:47234 DCHECK(bytes_read_ < kMaxBytesToSniff);
[email protected]e3c404b2008-12-23 01:07:32235 if (!finished_) {
236 buffering_ = true;
237 return true;
238 }
239 }
240 sniff_content_ = false;
241 response_->response_head.mime_type.assign(new_type);
242
243 // We just sniffed the mime type, maybe there is a doctype to process.
[email protected]35fa6a22009-08-15 00:04:01244 if (ShouldBuffer(request_->url(), new_type)) {
[email protected]e3c404b2008-12-23 01:07:32245 should_buffer_ = true;
[email protected]35fa6a22009-08-15 00:04:01246 } else if (ShouldWaitForPlugins()) {
247 wait_for_plugins_ = true;
248 }
[email protected]e3c404b2008-12-23 01:07:32249 }
250
[email protected]35fa6a22009-08-15 00:04:01251 if (should_buffer_) {
252 if (!finished_ && !DidBufferEnough(bytes_read_)) {
[email protected]e3c404b2008-12-23 01:07:32253 buffering_ = true;
254 return true;
255 }
[email protected]35fa6a22009-08-15 00:04:01256
257 should_buffer_ = false;
258 if (ShouldWaitForPlugins())
259 wait_for_plugins_ = true;
[email protected]e3c404b2008-12-23 01:07:32260 }
[email protected]35fa6a22009-08-15 00:04:01261
[email protected]e3c404b2008-12-23 01:07:32262 buffering_ = false;
[email protected]35fa6a22009-08-15 00:04:01263
264 if (wait_for_plugins_)
265 return true;
266
[email protected]e3c404b2008-12-23 01:07:32267 return false;
268}
269
270bool BufferedResourceHandler::CompleteResponseStarted(int request_id,
271 bool in_complete) {
272 // Check to see if we should forward the data from this request to the
273 // download thread.
274 // TODO(paulg): Only download if the context from the renderer allows it.
[email protected]e3c404b2008-12-23 01:07:32275 ResourceDispatcherHost::ExtraRequestInfo* info =
276 ResourceDispatcherHost::ExtraInfoForRequest(request_);
277
[email protected]35fa6a22009-08-15 00:04:01278 if (info->allow_download && ShouldDownload(NULL)) {
[email protected]e3c404b2008-12-23 01:07:32279 if (response_->response_head.headers && // Can be NULL if FTP.
280 response_->response_head.headers->response_code() / 100 != 2) {
281 // The response code indicates that this is an error page, but we don't
282 // know how to display the content. We follow Firefox here and show our
283 // own error page instead of triggering a download.
284 // TODO(abarth): We should abstract the response_code test, but this kind
285 // of check is scattered throughout our codebase.
[email protected]c4891b32009-03-08 07:41:31286 request_->SimulateError(net::ERR_FILE_NOT_FOUND);
[email protected]e3c404b2008-12-23 01:07:32287 return false;
288 }
289
290 info->is_download = true;
291
292 scoped_refptr<DownloadThrottlingResourceHandler> download_handler =
293 new DownloadThrottlingResourceHandler(host_,
294 request_,
[email protected]f6b48532009-02-12 01:56:32295 request_->url(),
[email protected]ad15f9e52009-03-10 22:44:13296 info->process_id,
297 info->route_id,
[email protected]e3c404b2008-12-23 01:07:32298 request_id,
299 in_complete);
300 if (bytes_read_) {
301 // a Read has already occurred and we need to copy the data into the
302 // EventHandler.
[email protected]9dea9e1f2009-01-29 00:30:47303 net::IOBuffer* buf = NULL;
[email protected]e3c404b2008-12-23 01:07:32304 int buf_len = 0;
305 download_handler->OnWillRead(request_id, &buf, &buf_len, bytes_read_);
306 CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0));
[email protected]9dea9e1f2009-01-29 00:30:47307 memcpy(buf->data(), read_buffer_->data(), bytes_read_);
[email protected]e3c404b2008-12-23 01:07:32308 }
[email protected]3c9481d2009-02-24 21:25:53309
310 // Send the renderer a response that indicates that the request will be
311 // handled by an external source (the browser's DownloadManager).
[email protected]e3c404b2008-12-23 01:07:32312 real_handler_->OnResponseStarted(info->request_id, response_);
[email protected]3c9481d2009-02-24 21:25:53313 URLRequestStatus status(URLRequestStatus::HANDLED_EXTERNALLY, 0);
[email protected]c4891b32009-03-08 07:41:31314 real_handler_->OnResponseCompleted(info->request_id, status,
315 std::string());
[email protected]3c9481d2009-02-24 21:25:53316
317 // Ditch the old async handler that talks to the renderer for the new
318 // download handler that talks to the DownloadManager.
[email protected]e3c404b2008-12-23 01:07:32319 real_handler_ = download_handler;
320 }
321 return real_handler_->OnResponseStarted(request_id, response_);
322}
323
[email protected]35fa6a22009-08-15 00:04:01324bool BufferedResourceHandler::ShouldWaitForPlugins() {
325 bool need_plugin_list;
326 if (!ShouldDownload(&need_plugin_list) || !need_plugin_list)
327 return false;
[email protected]e3c404b2008-12-23 01:07:32328
[email protected]35fa6a22009-08-15 00:04:01329 // We don't want to keep buffering as our buffer will fill up.
330 ResourceDispatcherHost::ExtraRequestInfo* info =
331 ResourceDispatcherHost::ExtraInfoForRequest(request_);
332 host_->PauseRequest(info->process_id, info->request_id, true);
333
334 // Schedule plugin loading on the file thread.
[email protected]f7a015e2009-08-15 02:50:19335 // Note: it's possible that the only reference to this object is the task. If
336 // If the task executes on the file thread, and before it returns, the task it
337 // posts to the IO thread runs, then this object will get destructed on the
338 // file thread. This breaks assumptions in other message handlers (i.e. when
339 // unregistering with NotificationService in the destructor).
340 AddRef();
[email protected]35fa6a22009-08-15 00:04:01341 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE,
[email protected]f7a015e2009-08-15 02:50:19342 NewRunnableFunction(&BufferedResourceHandler::LoadPlugins,
343 this, host_->ui_loop()));
[email protected]35fa6a22009-08-15 00:04:01344 return true;
345}
346
347// This test mirrors the decision that WebKit makes in
348// WebFrameLoaderClient::dispatchDecidePolicyForMIMEType.
349bool BufferedResourceHandler::ShouldDownload(bool* need_plugin_list) {
350 if (need_plugin_list)
351 *need_plugin_list = false;
352 std::string type = StringToLowerASCII(response_->response_head.mime_type);
353 std::string disposition;
354 request_->GetResponseHeaderByName("content-disposition", &disposition);
355 disposition = StringToLowerASCII(disposition);
356
357 // First, examine content-disposition.
358 if (!disposition.empty()) {
359 bool should_download = true;
360
361 // Some broken sites just send ...
362 // Content-Disposition: ; filename="file"
363 // ... screen those out here.
364 if (disposition[0] == ';')
365 should_download = false;
366
367 if (disposition.compare(0, 6, "inline") == 0)
368 should_download = false;
369
370 // Some broken sites just send ...
371 // Content-Disposition: filename="file"
372 // ... without a disposition token... Screen those out.
373 if (disposition.compare(0, 8, "filename") == 0)
374 should_download = false;
375
376 // Also in use is Content-Disposition: name="file"
377 if (disposition.compare(0, 4, "name") == 0)
378 should_download = false;
379
380 // We have a content-disposition of "attachment" or unknown.
381 // RFC 2183, section 2.8 says that an unknown disposition
382 // value should be treated as "attachment".
383 if (should_download)
384 return true;
385 }
386
387 // MIME type checking.
388 if (net::IsSupportedMimeType(type))
389 return false;
390
391 if (need_plugin_list) {
392 if (!NPAPI::PluginList::Singleton()->PluginsLoaded()) {
393 *need_plugin_list = true;
394 return true;
395 }
396 } else {
397 DCHECK(NPAPI::PluginList::Singleton()->PluginsLoaded());
398 }
399
400 // Finally, check the plugin list.
401 WebPluginInfo info;
402 bool allow_wildcard = false;
403 return !NPAPI::PluginList::Singleton()->GetPluginInfo(
404 GURL(), type, "", allow_wildcard, &info, NULL);
405}
406
[email protected]f7a015e2009-08-15 02:50:19407void BufferedResourceHandler::LoadPlugins(BufferedResourceHandler* handler,
408 MessageLoop* main_message_loop) {
[email protected]35fa6a22009-08-15 00:04:01409 std::vector<WebPluginInfo> plugins;
410 NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins);
[email protected]f7a015e2009-08-15 02:50:19411
412 // Note, we want to get to the IO thread now, but the file thread outlives it
413 // so we can't post a task to it directly as it might be in the middle of
414 // destruction. So hop through the main thread, where the destruction of the
415 // IO thread happens and hence no race conditions exist.
416 main_message_loop->PostTask(FROM_HERE,
417 NewRunnableFunction(&BufferedResourceHandler::NotifyPluginsLoaded,
418 handler));
419}
420
421void BufferedResourceHandler::NotifyPluginsLoaded(
422 BufferedResourceHandler* handler) {
423 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
424 NewRunnableMethod(handler, &BufferedResourceHandler::OnPluginsLoaded));
[email protected]35fa6a22009-08-15 00:04:01425}
426
427void BufferedResourceHandler::OnPluginsLoaded() {
[email protected]f7a015e2009-08-15 02:50:19428 Release();
[email protected]35fa6a22009-08-15 00:04:01429 wait_for_plugins_ = false;
430 if (!request_)
431 return;
432
433 ResourceDispatcherHost::ExtraRequestInfo* info =
434 ResourceDispatcherHost::ExtraInfoForRequest(request_);
435 host_->PauseRequest(info->process_id, info->request_id, false);
436 if (!CompleteResponseStarted(info->request_id, false))
437 host_->CancelRequest(info->process_id, info->request_id, false);
[email protected]e3c404b2008-12-23 01:07:32438}