Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(248)

Side by Side Diff: chrome/browser/renderer_host/buffered_resource_handler.cc

Issue 6532073: Move core pieces of browser\renderer_host to src\content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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
7 #include <vector>
8
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/string_util.h"
12 #include "chrome/browser/browser_thread.h"
13 #include "chrome/browser/renderer_host/download_throttling_resource_handler.h"
14 #include "chrome/browser/renderer_host/resource_dispatcher_host.h"
15 #include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h"
16 #include "chrome/browser/renderer_host/x509_user_cert_resource_handler.h"
17 #include "chrome/common/extensions/user_script.h"
18 #include "chrome/common/resource_response.h"
19 #include "chrome/common/url_constants.h"
20 #include "net/base/io_buffer.h"
21 #include "net/base/mime_sniffer.h"
22 #include "net/base/mime_util.h"
23 #include "net/base/net_errors.h"
24 #include "net/http/http_response_headers.h"
25 #include "webkit/plugins/npapi/plugin_list.h"
26
27 namespace {
28
29 void RecordSnifferMetrics(bool sniffing_blocked,
30 bool we_would_like_to_sniff,
31 const std::string& mime_type) {
32 static scoped_refptr<base::Histogram> nosniff_usage =
33 base::BooleanHistogram::FactoryGet(
34 "nosniff.usage", base::Histogram::kUmaTargetedHistogramFlag);
35 nosniff_usage->AddBoolean(sniffing_blocked);
36
37 if (sniffing_blocked) {
38 static scoped_refptr<base::Histogram> nosniff_otherwise =
39 base::BooleanHistogram::FactoryGet(
40 "nosniff.otherwise", base::Histogram::kUmaTargetedHistogramFlag);
41 nosniff_otherwise->AddBoolean(we_would_like_to_sniff);
42
43 static scoped_refptr<base::Histogram> nosniff_empty_mime_type =
44 base::BooleanHistogram::FactoryGet(
45 "nosniff.empty_mime_type",
46 base::Histogram::kUmaTargetedHistogramFlag);
47 nosniff_empty_mime_type->AddBoolean(mime_type.empty());
48 }
49 }
50
51 } // namespace
52
53 BufferedResourceHandler::BufferedResourceHandler(ResourceHandler* handler,
54 ResourceDispatcherHost* host,
55 net::URLRequest* request)
56 : real_handler_(handler),
57 host_(host),
58 request_(request),
59 read_buffer_size_(0),
60 bytes_read_(0),
61 sniff_content_(false),
62 should_buffer_(false),
63 wait_for_plugins_(false),
64 buffering_(false),
65 finished_(false) {
66 }
67
68 bool BufferedResourceHandler::OnUploadProgress(int request_id,
69 uint64 position,
70 uint64 size) {
71 return real_handler_->OnUploadProgress(request_id, position, size);
72 }
73
74 bool BufferedResourceHandler::OnRequestRedirected(int request_id,
75 const GURL& new_url,
76 ResourceResponse* response,
77 bool* defer) {
78 return real_handler_->OnRequestRedirected(
79 request_id, new_url, response, defer);
80 }
81
82 bool BufferedResourceHandler::OnResponseStarted(int request_id,
83 ResourceResponse* response) {
84 response_ = response;
85 if (!DelayResponse())
86 return CompleteResponseStarted(request_id, false);
87 return true;
88 }
89
90 bool BufferedResourceHandler::OnResponseCompleted(
91 int request_id,
92 const net::URLRequestStatus& status,
93 const std::string& security_info) {
94 return real_handler_->OnResponseCompleted(request_id, status, security_info);
95 }
96
97 void BufferedResourceHandler::OnRequestClosed() {
98 request_ = NULL;
99 real_handler_->OnRequestClosed();
100 }
101
102 bool BufferedResourceHandler::OnWillStart(int request_id,
103 const GURL& url,
104 bool* defer) {
105 return real_handler_->OnWillStart(request_id, url, defer);
106 }
107
108 // We'll let the original event handler provide a buffer, and reuse it for
109 // subsequent reads until we're done buffering.
110 bool BufferedResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
111 int* buf_size, int min_size) {
112 if (buffering_) {
113 DCHECK(!my_buffer_.get());
114 my_buffer_ = new net::IOBuffer(net::kMaxBytesToSniff);
115 *buf = my_buffer_.get();
116 *buf_size = net::kMaxBytesToSniff;
117 return true;
118 }
119
120 if (finished_)
121 return false;
122
123 if (!real_handler_->OnWillRead(request_id, buf, buf_size, min_size)) {
124 return false;
125 }
126 read_buffer_ = *buf;
127 read_buffer_size_ = *buf_size;
128 DCHECK_GE(read_buffer_size_, net::kMaxBytesToSniff * 2);
129 bytes_read_ = 0;
130 return true;
131 }
132
133 bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
134 if (sniff_content_ || should_buffer_) {
135 if (KeepBuffering(*bytes_read))
136 return true;
137
138 *bytes_read = bytes_read_;
139
140 // Done buffering, send the pending ResponseStarted event.
141 if (!CompleteResponseStarted(request_id, true))
142 return false;
143 } else if (wait_for_plugins_) {
144 return true;
145 }
146
147 // Release the reference that we acquired at OnWillRead.
148 read_buffer_ = NULL;
149 return real_handler_->OnReadCompleted(request_id, bytes_read);
150 }
151
152 BufferedResourceHandler::~BufferedResourceHandler() {}
153
154 bool BufferedResourceHandler::DelayResponse() {
155 std::string mime_type;
156 request_->GetMimeType(&mime_type);
157
158 std::string content_type_options;
159 request_->GetResponseHeaderByName("x-content-type-options",
160 &content_type_options);
161
162 const bool sniffing_blocked =
163 LowerCaseEqualsASCII(content_type_options, "nosniff");
164 const bool not_modified_status =
165 response_->response_head.headers &&
166 response_->response_head.headers->response_code() == 304;
167 const bool we_would_like_to_sniff = not_modified_status ?
168 false : net::ShouldSniffMimeType(request_->url(), mime_type);
169
170 RecordSnifferMetrics(sniffing_blocked, we_would_like_to_sniff, mime_type);
171
172 if (!sniffing_blocked && we_would_like_to_sniff) {
173 // We're going to look at the data before deciding what the content type
174 // is. That means we need to delay sending the ResponseStarted message
175 // over the IPC channel.
176 sniff_content_ = true;
177 VLOG(1) << "To buffer: " << request_->url().spec();
178 return true;
179 }
180
181 if (sniffing_blocked && mime_type.empty() && !not_modified_status) {
182 // Ugg. The server told us not to sniff the content but didn't give us a
183 // mime type. What's a browser to do? Turns out, we're supposed to treat
184 // the response as "text/plain". This is the most secure option.
185 mime_type.assign("text/plain");
186 response_->response_head.mime_type.assign(mime_type);
187 }
188
189 if (mime_type == "application/rss+xml" ||
190 mime_type == "application/atom+xml") {
191 // Sad face. The server told us that they wanted us to treat the response
192 // as RSS or Atom. Unfortunately, we don't have a built-in feed previewer
193 // like other browsers. We can't just render the content as XML because
194 // web sites let third parties inject arbitrary script into their RSS
195 // feeds. That leaves us with little choice but to practically ignore the
196 // response. In the future, when we have an RSS feed previewer, we can
197 // remove this logic.
198 mime_type.assign("text/plain");
199 response_->response_head.mime_type.assign(mime_type);
200 }
201
202 if (ShouldBuffer(request_->url(), mime_type)) {
203 // This is a temporary fix for the fact that webkit expects to have
204 // enough data to decode the doctype in order to select the rendering
205 // mode.
206 should_buffer_ = true;
207 return true;
208 }
209
210 if (!not_modified_status && ShouldWaitForPlugins()) {
211 wait_for_plugins_ = true;
212 return true;
213 }
214
215 return false;
216 }
217
218 bool BufferedResourceHandler::ShouldBuffer(const GURL& url,
219 const std::string& mime_type) {
220 // We are willing to buffer for HTTP and HTTPS.
221 bool sniffable_scheme = url.is_empty() ||
222 url.SchemeIs(chrome::kHttpScheme) ||
223 url.SchemeIs(chrome::kHttpsScheme);
224 if (!sniffable_scheme)
225 return false;
226
227 // Today, the only reason to buffer the request is to fix the doctype decoding
228 // performed by webkit: if there is not enough data it will go to quirks mode.
229 // We only expect the doctype check to apply to html documents.
230 return mime_type == "text/html";
231 }
232
233 bool BufferedResourceHandler::DidBufferEnough(int bytes_read) {
234 const int kRequiredLength = 256;
235
236 return bytes_read >= kRequiredLength;
237 }
238
239 bool BufferedResourceHandler::KeepBuffering(int bytes_read) {
240 DCHECK(read_buffer_);
241 if (my_buffer_) {
242 // We are using our own buffer to read, update the main buffer.
243 // TODO(darin): We should handle the case where read_buffer_size_ is small!
244 // See RedirectToFileResourceHandler::BufIsFull to see how this impairs
245 // downstream ResourceHandler implementations.
246 CHECK_LT(bytes_read + bytes_read_, read_buffer_size_);
247 memcpy(read_buffer_->data() + bytes_read_, my_buffer_->data(), bytes_read);
248 my_buffer_ = NULL;
249 }
250 bytes_read_ += bytes_read;
251 finished_ = (bytes_read == 0);
252
253 if (sniff_content_) {
254 std::string type_hint, new_type;
255 request_->GetMimeType(&type_hint);
256
257 if (!net::SniffMimeType(read_buffer_->data(), bytes_read_,
258 request_->url(), type_hint, &new_type)) {
259 // SniffMimeType() returns false if there is not enough data to determine
260 // the mime type. However, even if it returns false, it returns a new type
261 // that is probably better than the current one.
262 DCHECK_LT(bytes_read_, net::kMaxBytesToSniff);
263 if (!finished_) {
264 buffering_ = true;
265 return true;
266 }
267 }
268 sniff_content_ = false;
269 response_->response_head.mime_type.assign(new_type);
270
271 // We just sniffed the mime type, maybe there is a doctype to process.
272 if (ShouldBuffer(request_->url(), new_type)) {
273 should_buffer_ = true;
274 } else if (ShouldWaitForPlugins()) {
275 wait_for_plugins_ = true;
276 }
277 }
278
279 if (should_buffer_) {
280 if (!finished_ && !DidBufferEnough(bytes_read_)) {
281 buffering_ = true;
282 return true;
283 }
284
285 should_buffer_ = false;
286 if (ShouldWaitForPlugins())
287 wait_for_plugins_ = true;
288 }
289
290 buffering_ = false;
291
292 if (wait_for_plugins_)
293 return true;
294
295 return false;
296 }
297
298 bool BufferedResourceHandler::CompleteResponseStarted(int request_id,
299 bool in_complete) {
300 ResourceDispatcherHostRequestInfo* info =
301 ResourceDispatcherHost::InfoForRequest(request_);
302 std::string mime_type;
303 request_->GetMimeType(&mime_type);
304
305 // Check if this is an X.509 certificate, if yes, let it be handled
306 // by X509UserCertResourceHandler.
307 if (mime_type == "application/x-x509-user-cert") {
308 // This is entirely similar to how DownloadThrottlingResourceHandler
309 // works except we are doing it for an X.509 client certificates.
310
311 if (response_->response_head.headers && // Can be NULL if FTP.
312 response_->response_head.headers->response_code() / 100 != 2) {
313 // The response code indicates that this is an error page, but we are
314 // expecting an X.509 user certificate. We follow Firefox here and show
315 // our own error page instead of handling the error page as a
316 // certificate.
317 // TODO(abarth): We should abstract the response_code test, but this kind
318 // of check is scattered throughout our codebase.
319 request_->SimulateError(net::ERR_FILE_NOT_FOUND);
320 return false;
321 }
322
323 X509UserCertResourceHandler* x509_cert_handler =
324 new X509UserCertResourceHandler(host_, request_,
325 info->child_id(), info->route_id());
326 UseAlternateResourceHandler(request_id, x509_cert_handler);
327 }
328
329 // Check to see if we should forward the data from this request to the
330 // download thread.
331 // TODO(paulg): Only download if the context from the renderer allows it.
332 if (info->allow_download() && ShouldDownload(NULL)) {
333 if (response_->response_head.headers && // Can be NULL if FTP.
334 response_->response_head.headers->response_code() / 100 != 2) {
335 // The response code indicates that this is an error page, but we don't
336 // know how to display the content. We follow Firefox here and show our
337 // own error page instead of triggering a download.
338 // TODO(abarth): We should abstract the response_code test, but this kind
339 // of check is scattered throughout our codebase.
340 request_->SimulateError(net::ERR_FILE_NOT_FOUND);
341 return false;
342 }
343
344 info->set_is_download(true);
345
346 DownloadThrottlingResourceHandler* download_handler =
347 new DownloadThrottlingResourceHandler(host_,
348 request_,
349 request_->url(),
350 info->child_id(),
351 info->route_id(),
352 request_id,
353 in_complete);
354 UseAlternateResourceHandler(request_id, download_handler);
355 }
356 return real_handler_->OnResponseStarted(request_id, response_);
357 }
358
359 bool BufferedResourceHandler::ShouldWaitForPlugins() {
360 bool need_plugin_list;
361 if (!ShouldDownload(&need_plugin_list) || !need_plugin_list)
362 return false;
363
364 // We don't want to keep buffering as our buffer will fill up.
365 ResourceDispatcherHostRequestInfo* info =
366 ResourceDispatcherHost::InfoForRequest(request_);
367 host_->PauseRequest(info->child_id(), info->request_id(), true);
368
369 // Schedule plugin loading on the file thread.
370 BrowserThread::PostTask(
371 BrowserThread::FILE, FROM_HERE,
372 NewRunnableMethod(this, &BufferedResourceHandler::LoadPlugins));
373 return true;
374 }
375
376 // This test mirrors the decision that WebKit makes in
377 // WebFrameLoaderClient::dispatchDecidePolicyForMIMEType.
378 bool BufferedResourceHandler::ShouldDownload(bool* need_plugin_list) {
379 if (need_plugin_list)
380 *need_plugin_list = false;
381 std::string type = StringToLowerASCII(response_->response_head.mime_type);
382 std::string disposition;
383 request_->GetResponseHeaderByName("content-disposition", &disposition);
384 disposition = StringToLowerASCII(disposition);
385
386 // First, examine content-disposition.
387 if (!disposition.empty()) {
388 bool should_download = true;
389
390 // Some broken sites just send ...
391 // Content-Disposition: ; filename="file"
392 // ... screen those out here.
393 if (disposition[0] == ';')
394 should_download = false;
395
396 if (disposition.compare(0, 6, "inline") == 0)
397 should_download = false;
398
399 // Some broken sites just send ...
400 // Content-Disposition: filename="file"
401 // ... without a disposition token... Screen those out.
402 if (disposition.compare(0, 8, "filename") == 0)
403 should_download = false;
404
405 // Also in use is Content-Disposition: name="file"
406 if (disposition.compare(0, 4, "name") == 0)
407 should_download = false;
408
409 // We have a content-disposition of "attachment" or unknown.
410 // RFC 2183, section 2.8 says that an unknown disposition
411 // value should be treated as "attachment".
412 if (should_download)
413 return true;
414 }
415
416 // Special-case user scripts to get downloaded instead of viewed.
417 if (UserScript::HasUserScriptFileExtension(request_->url()))
418 return true;
419
420 // MIME type checking.
421 if (net::IsSupportedMimeType(type))
422 return false;
423
424 if (need_plugin_list) {
425 if (!webkit::npapi::PluginList::Singleton()->PluginsLoaded()) {
426 *need_plugin_list = true;
427 return true;
428 }
429 } else {
430 DCHECK(webkit::npapi::PluginList::Singleton()->PluginsLoaded());
431 }
432
433 // Finally, check the plugin list.
434 webkit::npapi::WebPluginInfo info;
435 bool allow_wildcard = false;
436 return !webkit::npapi::PluginList::Singleton()->GetPluginInfo(
437 GURL(), type, allow_wildcard, &info, NULL) ||
438 !webkit::npapi::IsPluginEnabled(info);
439 }
440
441 void BufferedResourceHandler::UseAlternateResourceHandler(
442 int request_id,
443 ResourceHandler* handler) {
444 ResourceDispatcherHostRequestInfo* info =
445 ResourceDispatcherHost::InfoForRequest(request_);
446 if (bytes_read_) {
447 // A Read has already occured and we need to copy the data into the new
448 // ResourceHandler.
449 net::IOBuffer* buf = NULL;
450 int buf_len = 0;
451 handler->OnWillRead(request_id, &buf, &buf_len, bytes_read_);
452 CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0));
453 memcpy(buf->data(), read_buffer_->data(), bytes_read_);
454 }
455
456 // Inform the original ResourceHandler that this will be handled entirely by
457 // the new ResourceHandler.
458 real_handler_->OnResponseStarted(info->request_id(), response_);
459 net::URLRequestStatus status(net::URLRequestStatus::HANDLED_EXTERNALLY, 0);
460 real_handler_->OnResponseCompleted(info->request_id(), status, std::string());
461
462 // Remove the non-owning pointer to the CrossSiteResourceHandler, if any,
463 // from the extra request info because the CrossSiteResourceHandler (part of
464 // the original ResourceHandler chain) will be deleted by the next statement.
465 info->set_cross_site_handler(NULL);
466
467 // This is handled entirely within the new ResourceHandler, so just reset the
468 // original ResourceHandler.
469 real_handler_ = handler;
470 }
471
472 void BufferedResourceHandler::LoadPlugins() {
473 std::vector<webkit::npapi::WebPluginInfo> plugins;
474 webkit::npapi::PluginList::Singleton()->GetPlugins(false, &plugins);
475
476 BrowserThread::PostTask(
477 BrowserThread::IO, FROM_HERE,
478 NewRunnableMethod(this, &BufferedResourceHandler::OnPluginsLoaded));
479 }
480
481 void BufferedResourceHandler::OnPluginsLoaded() {
482 wait_for_plugins_ = false;
483 if (!request_)
484 return;
485
486 ResourceDispatcherHostRequestInfo* info =
487 ResourceDispatcherHost::InfoForRequest(request_);
488 host_->PauseRequest(info->child_id(), info->request_id(), false);
489 if (!CompleteResponseStarted(info->request_id(), false))
490 host_->CancelRequest(info->child_id(), info->request_id(), false);
491 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698