OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "content/browser/renderer_host/buffered_resource_handler.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/logging.h" | |
11 #include "base/metrics/histogram.h" | |
12 #include "base/string_util.h" | |
13 #include "content/browser/download/download_resource_handler.h" | |
14 #include "content/browser/download/download_stats.h" | |
15 #include "content/browser/plugin_service_impl.h" | |
16 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" | |
17 #include "content/browser/renderer_host/resource_request_info_impl.h" | |
18 #include "content/browser/renderer_host/x509_user_cert_resource_handler.h" | |
19 #include "content/public/browser/browser_thread.h" | |
20 #include "content/public/browser/content_browser_client.h" | |
21 #include "content/public/browser/download_save_info.h" | |
22 #include "content/public/browser/resource_context.h" | |
23 #include "content/public/browser/resource_dispatcher_host_delegate.h" | |
24 #include "content/public/common/resource_response.h" | |
25 #include "net/base/io_buffer.h" | |
26 #include "net/base/mime_sniffer.h" | |
27 #include "net/base/mime_util.h" | |
28 #include "net/base/net_errors.h" | |
29 #include "net/http/http_content_disposition.h" | |
30 #include "net/http/http_response_headers.h" | |
31 #include "webkit/plugins/webplugininfo.h" | |
32 | |
33 namespace content { | |
34 | |
35 namespace { | |
36 | |
37 void RecordSnifferMetrics(bool sniffing_blocked, | |
38 bool we_would_like_to_sniff, | |
39 const std::string& mime_type) { | |
40 static base::Histogram* nosniff_usage(NULL); | |
41 if (!nosniff_usage) | |
42 nosniff_usage = base::BooleanHistogram::FactoryGet( | |
43 "nosniff.usage", base::Histogram::kUmaTargetedHistogramFlag); | |
44 nosniff_usage->AddBoolean(sniffing_blocked); | |
45 | |
46 if (sniffing_blocked) { | |
47 static base::Histogram* nosniff_otherwise(NULL); | |
48 if (!nosniff_otherwise) | |
49 nosniff_otherwise = base::BooleanHistogram::FactoryGet( | |
50 "nosniff.otherwise", base::Histogram::kUmaTargetedHistogramFlag); | |
51 nosniff_otherwise->AddBoolean(we_would_like_to_sniff); | |
52 | |
53 static base::Histogram* nosniff_empty_mime_type(NULL); | |
54 if (!nosniff_empty_mime_type) | |
55 nosniff_empty_mime_type = base::BooleanHistogram::FactoryGet( | |
56 "nosniff.empty_mime_type", | |
57 base::Histogram::kUmaTargetedHistogramFlag); | |
58 nosniff_empty_mime_type->AddBoolean(mime_type.empty()); | |
59 } | |
60 } | |
61 | |
62 // Used to write into an existing IOBuffer at a given offset. | |
63 class DependentIOBuffer : public net::WrappedIOBuffer { | |
64 public: | |
65 DependentIOBuffer(net::IOBuffer* buf, int offset) | |
66 : net::WrappedIOBuffer(buf->data() + offset), | |
67 buf_(buf) { | |
68 } | |
69 | |
70 private: | |
71 ~DependentIOBuffer() {} | |
72 | |
73 scoped_refptr<net::IOBuffer> buf_; | |
74 }; | |
75 | |
76 } // namespace | |
77 | |
78 BufferedResourceHandler::BufferedResourceHandler( | |
79 scoped_ptr<ResourceHandler> next_handler, | |
80 ResourceDispatcherHostImpl* host, | |
81 net::URLRequest* request) | |
82 : LayeredResourceHandler(next_handler.Pass()), | |
83 state_(STATE_STARTING), | |
84 host_(host), | |
85 request_(request), | |
86 read_buffer_size_(0), | |
87 bytes_read_(0), | |
88 must_download_(false), | |
89 must_download_is_set_(false), | |
90 weak_ptr_factory_(this) { | |
91 } | |
92 | |
93 BufferedResourceHandler::~BufferedResourceHandler() { | |
94 } | |
95 | |
96 void BufferedResourceHandler::SetController(ResourceController* controller) { | |
97 ResourceHandler::SetController(controller); | |
98 | |
99 // Downstream handlers see us as their ResourceController, which allows us to | |
100 // consume part or all of the resource response, and then later replay it to | |
101 // downstream handler. | |
102 DCHECK(next_handler_.get()); | |
103 next_handler_->SetController(this); | |
104 } | |
105 | |
106 bool BufferedResourceHandler::OnResponseStarted( | |
107 int request_id, | |
108 ResourceResponse* response, | |
109 bool* defer) { | |
110 response_ = response; | |
111 | |
112 // TODO(darin): It is very odd to special-case 304 responses at this level. | |
113 // We do so only because the code always has, see r24977 and r29355. The | |
114 // fact that 204 is no longer special-cased this way suggests that 304 need | |
115 // not be special-cased either. | |
116 // | |
117 // The network stack only forwards 304 responses that were not received in | |
118 // response to a conditional request (i.e., If-Modified-Since). Other 304 | |
119 // responses end up being translated to 200 or whatever the cached response | |
120 // code happens to be. It should be very rare to see a 304 at this level. | |
121 | |
122 if (!(response_->head.headers && | |
123 response_->head.headers->response_code() == 304)) { | |
124 if (ShouldSniffContent()) { | |
125 state_ = STATE_BUFFERING; | |
126 return true; | |
127 } | |
128 | |
129 if (response_->head.mime_type.empty()) { | |
130 // Ugg. The server told us not to sniff the content but didn't give us | |
131 // a mime type. What's a browser to do? Turns out, we're supposed to | |
132 // treat the response as "text/plain". This is the most secure option. | |
133 response_->head.mime_type.assign("text/plain"); | |
134 } | |
135 | |
136 // Treat feed types as text/plain. | |
137 if (response_->head.mime_type == "application/rss+xml" || | |
138 response_->head.mime_type == "application/atom+xml") { | |
139 response_->head.mime_type.assign("text/plain"); | |
140 } | |
141 } | |
142 | |
143 state_ = STATE_PROCESSING; | |
144 return ProcessResponse(defer); | |
145 } | |
146 | |
147 // We'll let the original event handler provide a buffer, and reuse it for | |
148 // subsequent reads until we're done buffering. | |
149 bool BufferedResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, | |
150 int* buf_size, int min_size) { | |
151 if (state_ == STATE_STREAMING) | |
152 return next_handler_->OnWillRead(request_id, buf, buf_size, min_size); | |
153 | |
154 DCHECK_EQ(-1, min_size); | |
155 | |
156 if (read_buffer_) { | |
157 CHECK_LT(bytes_read_, read_buffer_size_); | |
158 *buf = new DependentIOBuffer(read_buffer_, bytes_read_); | |
159 *buf_size = read_buffer_size_ - bytes_read_; | |
160 } else { | |
161 if (!next_handler_->OnWillRead(request_id, buf, buf_size, min_size)) | |
162 return false; | |
163 | |
164 read_buffer_ = *buf; | |
165 read_buffer_size_ = *buf_size; | |
166 DCHECK_GE(read_buffer_size_, net::kMaxBytesToSniff * 2); | |
167 } | |
168 return true; | |
169 } | |
170 | |
171 bool BufferedResourceHandler::OnReadCompleted(int request_id, int bytes_read, | |
172 bool* defer) { | |
173 if (state_ == STATE_STREAMING) | |
174 return next_handler_->OnReadCompleted(request_id, bytes_read, defer); | |
175 | |
176 DCHECK_EQ(state_, STATE_BUFFERING); | |
177 bytes_read_ += bytes_read; | |
178 | |
179 if (!DetermineMimeType() && (bytes_read > 0)) | |
180 return true; // Needs more data, so keep buffering. | |
181 | |
182 state_ = STATE_PROCESSING; | |
183 return ProcessResponse(defer); | |
184 } | |
185 | |
186 bool BufferedResourceHandler::OnResponseCompleted( | |
187 int request_id, | |
188 const net::URLRequestStatus& status, | |
189 const std::string& security_info) { | |
190 // Upon completion, act like a pass-through handler in case the downstream | |
191 // handler defers OnResponseCompleted. | |
192 state_ = STATE_STREAMING; | |
193 | |
194 return next_handler_->OnResponseCompleted(request_id, status, security_info); | |
195 } | |
196 | |
197 void BufferedResourceHandler::Resume() { | |
198 switch (state_) { | |
199 case STATE_BUFFERING: | |
200 case STATE_PROCESSING: | |
201 NOTREACHED(); | |
202 break; | |
203 case STATE_REPLAYING: | |
204 MessageLoop::current()->PostTask( | |
205 FROM_HERE, | |
206 base::Bind(&BufferedResourceHandler::CallReplayReadCompleted, | |
207 weak_ptr_factory_.GetWeakPtr())); | |
208 break; | |
209 case STATE_STARTING: | |
210 case STATE_STREAMING: | |
211 controller()->Resume(); | |
212 break; | |
213 } | |
214 } | |
215 | |
216 void BufferedResourceHandler::Cancel() { | |
217 controller()->Cancel(); | |
218 } | |
219 | |
220 void BufferedResourceHandler::CancelAndIgnore() { | |
221 controller()->CancelAndIgnore(); | |
222 } | |
223 | |
224 void BufferedResourceHandler::CancelWithError(int error_code) { | |
225 controller()->CancelWithError(error_code); | |
226 } | |
227 | |
228 bool BufferedResourceHandler::ProcessResponse(bool* defer) { | |
229 DCHECK_EQ(STATE_PROCESSING, state_); | |
230 | |
231 // TODO(darin): Stop special-casing 304 responses. | |
232 if (!(response_->head.headers && | |
233 response_->head.headers->response_code() == 304)) { | |
234 if (!SelectNextHandler(defer)) | |
235 return false; | |
236 if (*defer) | |
237 return true; | |
238 } | |
239 | |
240 state_ = STATE_REPLAYING; | |
241 | |
242 int request_id = ResourceRequestInfo::ForRequest(request_)->GetRequestID(); | |
243 if (!next_handler_->OnResponseStarted(request_id, response_, defer)) | |
244 return false; | |
245 | |
246 if (!read_buffer_) { | |
247 state_ = STATE_STREAMING; | |
248 return true; | |
249 } | |
250 | |
251 if (!*defer) | |
252 return ReplayReadCompleted(defer); | |
253 | |
254 return true; | |
255 } | |
256 | |
257 bool BufferedResourceHandler::ShouldSniffContent() { | |
258 const std::string& mime_type = response_->head.mime_type; | |
259 | |
260 std::string content_type_options; | |
261 request_->GetResponseHeaderByName("x-content-type-options", | |
262 &content_type_options); | |
263 | |
264 bool sniffing_blocked = | |
265 LowerCaseEqualsASCII(content_type_options, "nosniff"); | |
266 bool we_would_like_to_sniff = | |
267 net::ShouldSniffMimeType(request_->url(), mime_type); | |
268 | |
269 RecordSnifferMetrics(sniffing_blocked, we_would_like_to_sniff, mime_type); | |
270 | |
271 if (!sniffing_blocked && we_would_like_to_sniff) { | |
272 // We're going to look at the data before deciding what the content type | |
273 // is. That means we need to delay sending the ResponseStarted message | |
274 // over the IPC channel. | |
275 VLOG(1) << "To buffer: " << request_->url().spec(); | |
276 return true; | |
277 } | |
278 | |
279 return false; | |
280 } | |
281 | |
282 bool BufferedResourceHandler::DetermineMimeType() { | |
283 DCHECK_EQ(STATE_BUFFERING, state_); | |
284 | |
285 const std::string& type_hint = response_->head.mime_type; | |
286 | |
287 std::string new_type; | |
288 bool made_final_decision = | |
289 net::SniffMimeType(read_buffer_->data(), bytes_read_, request_->url(), | |
290 type_hint, &new_type); | |
291 | |
292 // SniffMimeType() returns false if there is not enough data to determine | |
293 // the mime type. However, even if it returns false, it returns a new type | |
294 // that is probably better than the current one. | |
295 response_->head.mime_type.assign(new_type); | |
296 | |
297 return made_final_decision; | |
298 } | |
299 | |
300 bool BufferedResourceHandler::SelectNextHandler(bool* defer) { | |
301 DCHECK(!response_->head.mime_type.empty()); | |
302 | |
303 ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request_); | |
304 const std::string& mime_type = response_->head.mime_type; | |
305 | |
306 if (mime_type == "application/x-x509-user-cert") { | |
307 // Install X509 handler. | |
308 scoped_ptr<ResourceHandler> handler( | |
309 new X509UserCertResourceHandler(request_, | |
310 info->GetChildID(), | |
311 info->GetRouteID())); | |
312 return UseAlternateNextHandler(handler.Pass()); | |
313 } | |
314 | |
315 if (!info->allow_download()) | |
316 return true; | |
317 | |
318 if (!MustDownload()) { | |
319 if (net::IsSupportedMimeType(mime_type)) | |
320 return true; | |
321 | |
322 bool stale; | |
323 bool has_plugin = HasSupportingPlugin(&stale); | |
324 if (stale) { | |
325 // Refresh the plugins asynchronously. | |
326 PluginServiceImpl::GetInstance()->GetPlugins( | |
327 base::Bind(&BufferedResourceHandler::OnPluginsLoaded, | |
328 weak_ptr_factory_.GetWeakPtr())); | |
329 *defer = true; | |
330 return true; | |
331 } | |
332 if (has_plugin) | |
333 return true; | |
334 } | |
335 | |
336 // Install download handler | |
337 info->set_is_download(true); | |
338 scoped_ptr<ResourceHandler> handler( | |
339 host_->CreateResourceHandlerForDownload( | |
340 request_, | |
341 true, // is_content_initiated | |
342 scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()), | |
343 DownloadResourceHandler::OnStartedCallback())); | |
344 return UseAlternateNextHandler(handler.Pass()); | |
345 } | |
346 | |
347 bool BufferedResourceHandler::UseAlternateNextHandler( | |
348 scoped_ptr<ResourceHandler> new_handler) { | |
349 if (response_->head.headers && // Can be NULL if FTP. | |
350 response_->head.headers->response_code() / 100 != 2) { | |
351 // The response code indicates that this is an error page, but we don't | |
352 // know how to display the content. We follow Firefox here and show our | |
353 // own error page instead of triggering a download. | |
354 // TODO(abarth): We should abstract the response_code test, but this kind | |
355 // of check is scattered throughout our codebase. | |
356 request_->CancelWithError(net::ERR_FILE_NOT_FOUND); | |
357 return false; | |
358 } | |
359 | |
360 int request_id = ResourceRequestInfo::ForRequest(request_)->GetRequestID(); | |
361 | |
362 // Inform the original ResourceHandler that this will be handled entirely by | |
363 // the new ResourceHandler. | |
364 // TODO(darin): We should probably check the return values of these. | |
365 bool defer_ignored = false; | |
366 next_handler_->OnResponseStarted(request_id, response_, &defer_ignored); | |
367 DCHECK(!defer_ignored); | |
368 net::URLRequestStatus status(net::URLRequestStatus::CANCELED, | |
369 net::ERR_ABORTED); | |
370 next_handler_->OnResponseCompleted(request_id, status, std::string()); | |
371 | |
372 // This is handled entirely within the new ResourceHandler, so just reset the | |
373 // original ResourceHandler. | |
374 next_handler_ = new_handler.Pass(); | |
375 next_handler_->SetController(this); | |
376 | |
377 return CopyReadBufferToNextHandler(request_id); | |
378 } | |
379 | |
380 bool BufferedResourceHandler::ReplayReadCompleted(bool* defer) { | |
381 DCHECK(read_buffer_); | |
382 | |
383 int request_id = ResourceRequestInfo::ForRequest(request_)->GetRequestID(); | |
384 bool result = next_handler_->OnReadCompleted(request_id, bytes_read_, defer); | |
385 | |
386 read_buffer_ = NULL; | |
387 read_buffer_size_ = 0; | |
388 bytes_read_ = 0; | |
389 | |
390 state_ = STATE_STREAMING; | |
391 | |
392 return result; | |
393 } | |
394 | |
395 void BufferedResourceHandler::CallReplayReadCompleted() { | |
396 bool defer = false; | |
397 if (!ReplayReadCompleted(&defer)) { | |
398 controller()->Cancel(); | |
399 } else if (!defer) { | |
400 state_ = STATE_STREAMING; | |
401 controller()->Resume(); | |
402 } | |
403 } | |
404 | |
405 bool BufferedResourceHandler::MustDownload() { | |
406 if (must_download_is_set_) | |
407 return must_download_; | |
408 | |
409 must_download_is_set_ = true; | |
410 | |
411 std::string disposition; | |
412 request_->GetResponseHeaderByName("content-disposition", &disposition); | |
413 if (!disposition.empty() && | |
414 net::HttpContentDisposition(disposition, std::string()).is_attachment()) { | |
415 must_download_ = true; | |
416 } else if (host_->delegate() && | |
417 host_->delegate()->ShouldForceDownloadResource( | |
418 request_->url(), response_->head.mime_type)) { | |
419 must_download_ = true; | |
420 } else { | |
421 must_download_ = false; | |
422 } | |
423 | |
424 return must_download_; | |
425 } | |
426 | |
427 bool BufferedResourceHandler::HasSupportingPlugin(bool* stale) { | |
428 ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request_); | |
429 | |
430 bool allow_wildcard = false; | |
431 webkit::WebPluginInfo plugin; | |
432 return PluginServiceImpl::GetInstance()->GetPluginInfo( | |
433 info->GetChildID(), info->GetRouteID(), info->GetContext(), | |
434 request_->url(), GURL(), response_->head.mime_type, allow_wildcard, | |
435 stale, &plugin, NULL); | |
436 } | |
437 | |
438 bool BufferedResourceHandler::CopyReadBufferToNextHandler(int request_id) { | |
439 if (!bytes_read_) | |
440 return true; | |
441 | |
442 net::IOBuffer* buf = NULL; | |
443 int buf_len = 0; | |
444 if (!next_handler_->OnWillRead(request_id, &buf, &buf_len, bytes_read_)) | |
445 return false; | |
446 | |
447 CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0)); | |
448 memcpy(buf->data(), read_buffer_->data(), bytes_read_); | |
449 return true; | |
450 } | |
451 | |
452 void BufferedResourceHandler::OnPluginsLoaded( | |
453 const std::vector<webkit::WebPluginInfo>& plugins) { | |
454 bool defer = false; | |
455 if (!ProcessResponse(&defer)) { | |
456 controller()->Cancel(); | |
457 } else if (!defer) { | |
458 controller()->Resume(); | |
459 } | |
460 } | |
461 | |
462 } // namespace content | |
OLD | NEW |