1 // Copyright (c) 2013 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.
5 #include "content/child/npapi/plugin_url_fetcher.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "content/child/child_thread_impl.h"
9 #include "content/child/multipart_response_delegate.h"
10 #include "content/child/npapi/plugin_host.h"
11 #include "content/child/npapi/plugin_instance.h"
12 #include "content/child/npapi/plugin_stream_url.h"
13 #include "content/child/npapi/webplugin.h"
14 #include "content/child/npapi/webplugin_resource_client.h"
15 #include "content/child/plugin_messages.h"
16 #include "content/child/request_extra_data.h"
17 #include "content/child/request_info.h"
18 #include "content/child/resource_dispatcher.h"
19 #include "content/child/web_url_loader_impl.h"
20 #include "content/common/resource_request_body.h"
21 #include "content/common/service_worker/service_worker_types.h"
22 #include "content/public/common/resource_response_info.h"
23 #include "net/base/load_flags.h"
24 #include "net/base/net_errors.h"
25 #include "net/http/http_response_headers.h"
26 #include "net/url_request/redirect_info.h"
27 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
28 #include "third_party/WebKit/public/platform/WebURLResponse.h"
33 // This class handles individual multipart responses. It is instantiated when
34 // we receive HTTP status code 206 in the HTTP response. This indicates
35 // that the response could have multiple parts each separated by a boundary
36 // specified in the response header.
37 // TODO(jam): this is similar to MultiPartResponseClient in webplugin_impl.cc,
38 // we should remove that other class once we switch to loading from the plugin
39 // process by default.
40 class MultiPartResponseClient
: public blink::WebURLLoaderClient
{
42 explicit MultiPartResponseClient(PluginStreamUrl
* plugin_stream
)
43 : byte_range_lower_bound_(0), plugin_stream_(plugin_stream
) {}
45 // blink::WebURLLoaderClient implementation:
46 void didReceiveResponse(blink::WebURLLoader
* loader
,
47 const blink::WebURLResponse
& response
) override
{
48 int64 byte_range_upper_bound
, instance_size
;
49 if (!MultipartResponseDelegate::ReadContentRanges(response
,
50 &byte_range_lower_bound_
,
51 &byte_range_upper_bound
,
56 void didReceiveData(blink::WebURLLoader
* loader
,
59 int encoded_data_length
) override
{
61 // We should defer further loads on multipart resources on the same lines
62 // as regular resources requested by plugins to prevent reentrancy.
63 int64 data_offset
= byte_range_lower_bound_
;
64 byte_range_lower_bound_
+= data_length
;
65 plugin_stream_
->DidReceiveData(data
, data_length
, data_offset
);
66 // DANGER: this instance may be deleted at this point.
70 // The lower bound of the byte range.
71 int64 byte_range_lower_bound_
;
72 // The handler for the data.
73 PluginStreamUrl
* plugin_stream_
;
78 PluginURLFetcher::PluginURLFetcher(PluginStreamUrl
* plugin_stream
,
80 const GURL
& first_party_for_cookies
,
81 const std::string
& method
,
84 const Referrer
& referrer
,
85 const std::string
& range
,
86 bool notify_redirects
,
87 bool is_plugin_src_load
,
91 unsigned long resource_id
,
92 bool copy_stream_data
)
93 : plugin_stream_(plugin_stream
),
95 first_party_for_cookies_(first_party_for_cookies
),
97 notify_redirects_(notify_redirects
),
98 is_plugin_src_load_(is_plugin_src_load
),
99 origin_pid_(origin_pid
),
100 render_frame_id_(render_frame_id
),
101 render_view_id_(render_view_id
),
102 resource_id_(resource_id
),
103 copy_stream_data_(copy_stream_data
),
105 pending_failure_notification_(false),
107 RequestInfo request_info
;
108 request_info
.method
= method
;
109 request_info
.url
= url
;
110 request_info
.first_party_for_cookies
= first_party_for_cookies
;
111 request_info
.referrer
= referrer
;
112 request_info
.load_flags
= net::LOAD_NORMAL
;
113 request_info
.requestor_pid
= origin_pid
;
114 request_info
.request_type
= RESOURCE_TYPE_OBJECT
;
115 request_info
.routing_id
= render_view_id
;
116 // ServiceWorker is disabled for NPAPI.
117 request_info
.skip_service_worker
= true;
119 RequestExtraData extra_data
;
120 extra_data
.set_render_frame_id(render_frame_id
);
121 extra_data
.set_is_main_frame(false);
122 request_info
.extra_data
= &extra_data
;
124 std::vector
<char> body
;
125 if (method
== "POST") {
126 bool content_type_found
= false;
127 std::vector
<std::string
> names
;
128 std::vector
<std::string
> values
;
129 PluginHost::SetPostData(buf
, len
, &names
, &values
, &body
);
130 for (size_t i
= 0; i
< names
.size(); ++i
) {
131 if (!request_info
.headers
.empty())
132 request_info
.headers
+= "\r\n";
133 request_info
.headers
+= names
[i
] + ": " + values
[i
];
134 if (base::LowerCaseEqualsASCII(names
[i
], "content-type"))
135 content_type_found
= true;
138 if (!content_type_found
) {
139 if (!request_info
.headers
.empty())
140 request_info
.headers
+= "\r\n";
141 request_info
.headers
+= "Content-Type: application/x-www-form-urlencoded";
145 request_info
.headers
= std::string("Range: ") + range
;
148 scoped_refptr
<ResourceRequestBody
> request_body
= new ResourceRequestBody
;
150 request_body
->AppendBytes(&body
[0], body
.size());
152 request_id_
= ChildThreadImpl::current()->resource_dispatcher()->StartAsync(
153 request_info
, request_body
.get(), this);
155 // TODO(jam): range requests
158 PluginURLFetcher::~PluginURLFetcher() {
159 if (request_id_
>= 0) {
160 ChildThreadImpl::current()->resource_dispatcher()->RemovePendingRequest(
165 void PluginURLFetcher::Cancel() {
166 ChildThreadImpl::current()->resource_dispatcher()->Cancel(request_id_
);
168 // Due to races and nested event loops, PluginURLFetcher may still receive
169 // events from the bridge before being destroyed. Do not forward additional
170 // events back to the plugin, via either |plugin_stream_| or
171 // |multipart_delegate_| which has its own pointer via
172 // MultiPartResponseClient.
173 if (multipart_delegate_
)
174 multipart_delegate_
->Cancel();
175 plugin_stream_
= NULL
;
178 void PluginURLFetcher::URLRedirectResponse(bool allow
) {
183 ChildThreadImpl::current()->resource_dispatcher()->SetDefersLoading(
186 ChildThreadImpl::current()->resource_dispatcher()->Cancel(request_id_
);
187 plugin_stream_
->DidFail(resource_id_
); // That will delete |this|.
191 void PluginURLFetcher::OnUploadProgress(uint64 position
, uint64 size
) {
194 bool PluginURLFetcher::OnReceivedRedirect(
195 const net::RedirectInfo
& redirect_info
,
196 const ResourceResponseInfo
& info
) {
200 // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::willSendRequest until
201 // kDirectNPAPIRequests is the default and we can remove the old path there.
203 // Currently this check is just to catch an https -> http redirect when
204 // loading the main plugin src URL. Longer term, we could investigate
205 // firing mixed diplay or scripting issues for subresource loads
206 // initiated by plugins.
207 if (is_plugin_src_load_
&&
208 !plugin_stream_
->instance()->webplugin()->CheckIfRunInsecureContent(
209 redirect_info
.new_url
)) {
210 plugin_stream_
->DidFail(resource_id_
); // That will delete |this|.
215 url_
= redirect_info
.new_url
;
216 first_party_for_cookies_
= redirect_info
.new_first_party_for_cookies
;
218 // If the plugin does not participate in url redirect notifications then just
219 // block cross origin 307 POST redirects.
220 if (!notify_redirects_
) {
221 if (redirect_info
.status_code
== 307 &&
222 redirect_info
.new_method
== "POST" &&
223 old_url
.GetOrigin() != url_
.GetOrigin()) {
224 plugin_stream_
->DidFail(resource_id_
); // That will delete |this|.
228 // Pause the request while we ask the plugin what to do about the redirect.
229 ChildThreadImpl::current()->resource_dispatcher()->SetDefersLoading(
231 plugin_stream_
->WillSendRequest(url_
, redirect_info
.status_code
);
237 void PluginURLFetcher::OnReceivedResponse(const ResourceResponseInfo
& info
) {
241 // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::didReceiveResponse
242 // GetAllHeaders, and GetResponseInfo until kDirectNPAPIRequests is the
243 // default and we can remove the old path there.
245 bool request_is_seekable
= true;
246 DCHECK(!multipart_delegate_
.get());
247 if (plugin_stream_
->seekable()) {
248 int response_code
= info
.headers
->response_code();
249 if (response_code
== 206) {
250 blink::WebURLResponse response
;
251 response
.initialize();
252 WebURLLoaderImpl::PopulateURLResponse(url_
, info
, &response
);
254 std::string multipart_boundary
;
255 if (MultipartResponseDelegate::ReadMultipartBoundary(
256 response
, &multipart_boundary
)) {
257 plugin_stream_
->instance()->webplugin()->DidStartLoading();
259 MultiPartResponseClient
* multi_part_response_client
=
260 new MultiPartResponseClient(plugin_stream_
);
262 multipart_delegate_
.reset(new MultipartResponseDelegate(
263 multi_part_response_client
, NULL
, response
, multipart_boundary
));
265 // Multiple ranges requested, data will be delivered by
266 // MultipartResponseDelegate.
271 int64 upper_bound
= 0, instance_size
= 0;
272 // Single range requested - go through original processing for
273 // non-multipart requests, but update data offset.
274 MultipartResponseDelegate::ReadContentRanges(
275 response
, &data_offset_
, &upper_bound
, &instance_size
);
276 } else if (response_code
== 200) {
277 // TODO: should we handle this case? We used to but it's not clear that we
278 // still need to. This was bug 5403, fixed in r7139.
282 // If the length comes in as -1, then it indicates that it was not
283 // read off the HTTP headers. We replicate Safari webkit behavior here,
284 // which is to set it to 0.
285 int expected_length
= std::max(static_cast<int>(info
.content_length
), 0);
288 uint32 last_modified
= 0;
290 if (info
.headers
.get()) { // NULL for data: urls.
291 if (info
.headers
->GetLastModifiedValue(&temp
))
292 last_modified
= static_cast<uint32
>(temp
.ToDoubleT());
294 // TODO(darin): Shouldn't we also report HTTP version numbers?
295 int response_code
= info
.headers
->response_code();
296 headers
= base::StringPrintf("HTTP %d ", response_code
);
297 headers
+= info
.headers
->GetStatusText();
301 std::string name
, value
;
302 while (info
.headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
303 // TODO(darin): Should we really exclude headers with an empty value?
304 if (!name
.empty() && !value
.empty())
305 headers
+= name
+ ": " + value
+ "\n";
308 // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP
309 // error codes in the stream header and as a result, was unaware of the fate
310 // of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF destroy
311 // the stream and invoke the NPP_DestroyStream function on the plugin if the
312 // HTTPrequest fails.
313 if ((url_
.SchemeIs(url::kHttpScheme
) || url_
.SchemeIs(url::kHttpsScheme
)) &&
314 (response_code
< 100 || response_code
>= 400)) {
315 pending_failure_notification_
= true;
319 plugin_stream_
->DidReceiveResponse(info
.mime_type
,
323 request_is_seekable
);
326 void PluginURLFetcher::OnDownloadedData(int len
,
327 int encoded_data_length
) {
330 void PluginURLFetcher::OnReceivedData(scoped_ptr
<ReceivedData
> data
) {
331 const char* payload
= data
->payload();
332 int data_length
= data
->length();
333 int encoded_data_length
= data
->encoded_length();
337 if (multipart_delegate_
) {
338 multipart_delegate_
->OnReceivedData(payload
, data_length
,
339 encoded_data_length
);
341 int64 offset
= data_offset_
;
342 data_offset_
+= data_length
;
344 if (copy_stream_data_
) {
345 // QuickTime writes to this memory, and since we got it from
346 // ResourceDispatcher it's not mapped for write access in this process.
347 // http://crbug.com/308466.
348 scoped_ptr
<char[]> data_copy(new char[data_length
]);
349 memcpy(data_copy
.get(), payload
, data_length
);
350 plugin_stream_
->DidReceiveData(data_copy
.get(), data_length
, offset
);
352 plugin_stream_
->DidReceiveData(payload
, data_length
, offset
);
354 // DANGER: this instance may be deleted at this point.
358 void PluginURLFetcher::OnCompletedRequest(
360 bool was_ignored_by_handler
,
361 bool stale_copy_in_cache
,
362 const std::string
& security_info
,
363 const base::TimeTicks
& completion_time
,
364 int64 total_transfer_size
) {
368 if (multipart_delegate_
) {
369 multipart_delegate_
->OnCompletedRequest();
370 multipart_delegate_
.reset();
373 if (error_code
== net::OK
) {
374 plugin_stream_
->DidFinishLoading(resource_id_
);
376 plugin_stream_
->DidFail(resource_id_
);
380 } // namespace content