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.
5 #include "content/renderer/pepper/url_request_info_util.h"
7 #include "base/logging.h"
8 #include "base/strings/string_util.h"
9 #include "content/child/request_extra_data.h"
10 #include "content/common/fileapi/file_system_messages.h"
11 #include "content/renderer/pepper/host_globals.h"
12 #include "content/renderer/pepper/pepper_file_ref_renderer_host.h"
13 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
14 #include "content/renderer/pepper/plugin_module.h"
15 #include "content/renderer/pepper/renderer_ppapi_host_impl.h"
16 #include "content/renderer/render_thread_impl.h"
17 #include "net/http/http_util.h"
18 #include "ppapi/c/pp_bool.h"
19 #include "ppapi/c/pp_var.h"
20 #include "ppapi/proxy/ppapi_messages.h"
21 #include "ppapi/shared_impl/url_request_info_data.h"
22 #include "ppapi/shared_impl/var.h"
23 #include "ppapi/thunk/enter.h"
24 #include "third_party/WebKit/public/platform/WebData.h"
25 #include "third_party/WebKit/public/platform/WebHTTPBody.h"
26 #include "third_party/WebKit/public/platform/WebURL.h"
27 #include "third_party/WebKit/public/platform/WebURLRequest.h"
28 #include "third_party/WebKit/public/web/WebDocument.h"
29 #include "third_party/WebKit/public/web/WebFrame.h"
31 #include "url/url_util.h"
33 using ppapi::Resource
;
34 using ppapi::URLRequestInfoData
;
35 using ppapi::thunk::EnterResourceNoLock
;
37 using blink::WebHTTPBody
;
38 using blink::WebString
;
39 using blink::WebFrame
;
41 using blink::WebURLRequest
;
47 // Appends the file ref given the Resource pointer associated with it to the
48 // given HTTP body, returning true on success.
49 bool AppendFileRefToBody(PP_Instance instance
,
52 int64_t number_of_bytes
,
53 PP_Time expected_last_modified_time
,
54 WebHTTPBody
* http_body
) {
55 base::FilePath platform_path
;
56 PepperPluginInstanceImpl
* instance_impl
=
57 HostGlobals::Get()->GetInstance(instance
);
61 RendererPpapiHost
* renderer_ppapi_host
=
62 instance_impl
->module()->renderer_ppapi_host();
63 if (!renderer_ppapi_host
)
65 ppapi::host::ResourceHost
* resource_host
=
66 renderer_ppapi_host
->GetPpapiHost()->GetResourceHost(resource
);
67 if (!resource_host
|| !resource_host
->IsFileRefHost())
69 PepperFileRefRendererHost
* file_ref_host
=
70 static_cast<PepperFileRefRendererHost
*>(resource_host
);
71 switch (file_ref_host
->GetFileSystemType()) {
72 case PP_FILESYSTEMTYPE_LOCALTEMPORARY
:
73 case PP_FILESYSTEMTYPE_LOCALPERSISTENT
:
74 // TODO(kinuko): remove this sync IPC when we fully support
75 // AppendURLRange for FileSystem URL.
76 RenderThreadImpl::current()->Send(
77 new FileSystemHostMsg_SyncGetPlatformPath(
78 file_ref_host
->GetFileSystemURL(), &platform_path
));
80 case PP_FILESYSTEMTYPE_EXTERNAL
:
81 platform_path
= file_ref_host
->GetExternalFilePath();
86 http_body
->appendFileRange(platform_path
.AsUTF16Unsafe(),
89 expected_last_modified_time
);
93 // Checks that the request data is valid. Returns false on failure. Note that
94 // method and header validation is done by the URL loader when the request is
95 // opened, and any access errors are returned asynchronously.
96 bool ValidateURLRequestData(const URLRequestInfoData
& data
) {
97 if (data
.prefetch_buffer_lower_threshold
< 0 ||
98 data
.prefetch_buffer_upper_threshold
< 0 ||
99 data
.prefetch_buffer_upper_threshold
<=
100 data
.prefetch_buffer_lower_threshold
) {
106 std::string
FilterStringForXRequestedWithValue(const std::string
& s
) {
108 rv
.reserve(s
.length());
109 for (size_t i
= 0; i
< s
.length(); i
++) {
111 // Allow ASCII digits, letters, periods, commas, and underscores. (Ignore
112 // all other characters.)
113 if ((c
>= '0' && c
<= '9') || (c
>= 'A' && c
<= 'Z') ||
114 (c
>= 'a' && c
<= 'z') || (c
== '.') || (c
== ',') || (c
== '_'))
120 // Returns an appropriate value for the X-Requested-With header for plugins that
121 // present an X-Requested-With header. Returns a blank string for other plugins.
122 // We produce a user-agent-like string (eating spaces and other undesired
123 // characters) like "ShockwaveFlash/11.5.31.135" from the plugin name and
125 std::string
MakeXRequestedWithValue(const std::string
& name
,
126 const std::string
& version
) {
127 std::string rv
= FilterStringForXRequestedWithValue(name
);
129 return std::string();
131 // Apply to a narrow list of plugins only.
132 if (rv
!= "ShockwaveFlash" && rv
!= "PPAPITests")
133 return std::string();
135 std::string filtered_version
= FilterStringForXRequestedWithValue(version
);
136 if (!filtered_version
.empty())
137 rv
+= "/" + filtered_version
;
144 bool CreateWebURLRequest(PP_Instance instance
,
145 URLRequestInfoData
* data
,
147 WebURLRequest
* dest
) {
148 // In the out-of-process case, we've received the URLRequestInfoData
149 // from the untrusted plugin and done no validation on it. We need to be
150 // sure it's not being malicious by checking everything for consistency.
151 if (!ValidateURLRequestData(*data
))
154 std::string name_version
;
156 // Allow instance to be 0 or -1 for testing purposes.
157 if (instance
&& instance
!= -1) {
158 PepperPluginInstanceImpl
* instance_impl
=
159 HostGlobals::Get()->GetInstance(instance
);
161 name_version
= MakeXRequestedWithValue(
162 instance_impl
->module()->name(),
163 instance_impl
->module()->version());
166 name_version
= "internal_testing_only";
170 dest
->setURL(frame
->document().completeURL(WebString::fromUTF8(data
->url
)));
171 dest
->setDownloadToFile(data
->stream_to_file
);
172 dest
->setReportUploadProgress(data
->record_upload_progress
);
174 if (!data
->method
.empty())
175 dest
->setHTTPMethod(WebString::fromUTF8(data
->method
));
177 dest
->setFirstPartyForCookies(frame
->document().firstPartyForCookies());
179 const std::string
& headers
= data
->headers
;
180 if (!headers
.empty()) {
181 net::HttpUtil::HeadersIterator
it(headers
.begin(), headers
.end(), "\n\r");
182 while (it
.GetNext()) {
183 dest
->addHTTPHeaderField(WebString::fromUTF8(it
.name()),
184 WebString::fromUTF8(it
.values()));
188 // Append the upload data.
189 if (!data
->body
.empty()) {
190 WebHTTPBody http_body
;
191 http_body
.initialize();
193 for (size_t i
= 0; i
< data
->body
.size(); ++i
) {
194 const URLRequestInfoData::BodyItem
& item
= data
->body
[i
];
196 if (!AppendFileRefToBody(instance
,
197 item
.file_ref_pp_resource
,
199 item
.number_of_bytes
,
200 item
.expected_last_modified_time
,
205 DCHECK(!item
.data
.empty());
206 http_body
.appendData(WebData(item
.data
));
209 dest
->setHTTPBody(http_body
);
212 // Add the "Referer" header if there is a custom referrer. Such requests
213 // require universal access. For all other requests, "Referer" will be set
214 // after header security checks are done in AssociatedURLLoader.
215 if (data
->has_custom_referrer_url
&& !data
->custom_referrer_url
.empty())
216 frame
->setReferrerForRequest(*dest
, GURL(data
->custom_referrer_url
));
218 if (data
->has_custom_content_transfer_encoding
&&
219 !data
->custom_content_transfer_encoding
.empty()) {
220 dest
->addHTTPHeaderField(
221 WebString::fromUTF8("Content-Transfer-Encoding"),
222 WebString::fromUTF8(data
->custom_content_transfer_encoding
));
225 if (data
->has_custom_user_agent
|| !name_version
.empty()) {
226 RequestExtraData
* extra_data
= new RequestExtraData();
227 if (data
->has_custom_user_agent
) {
228 extra_data
->set_custom_user_agent(
229 WebString::fromUTF8(data
->custom_user_agent
));
231 if (!name_version
.empty()) {
232 extra_data
->set_requested_with(WebString::fromUTF8(name_version
));
234 dest
->setExtraData(extra_data
);
240 bool URLRequestRequiresUniversalAccess(const URLRequestInfoData
& data
) {
241 return data
.has_custom_referrer_url
||
242 data
.has_custom_content_transfer_encoding
||
243 data
.has_custom_user_agent
||
244 url::FindAndCompareScheme(data
.url
, url::kJavaScriptScheme
, NULL
);
247 } // namespace content