Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / renderer / pepper / url_request_info_util.cc
blob4b70190b9bae263befc52c0c4344656eda23b6f8
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"
30 #include "url/gurl.h"
31 #include "url/url_util.h"
33 using ppapi::Resource;
34 using ppapi::URLRequestInfoData;
35 using ppapi::thunk::EnterResourceNoLock;
36 using blink::WebData;
37 using blink::WebHTTPBody;
38 using blink::WebString;
39 using blink::WebFrame;
40 using blink::WebURL;
41 using blink::WebURLRequest;
43 namespace content {
45 namespace {
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,
50 PP_Resource resource,
51 int64_t start_offset,
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);
58 if (!instance_impl)
59 return false;
61 RendererPpapiHost* renderer_ppapi_host =
62 instance_impl->module()->renderer_ppapi_host();
63 if (!renderer_ppapi_host)
64 return false;
65 ppapi::host::ResourceHost* resource_host =
66 renderer_ppapi_host->GetPpapiHost()->GetResourceHost(resource);
67 if (!resource_host || !resource_host->IsFileRefHost())
68 return false;
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));
79 break;
80 case PP_FILESYSTEMTYPE_EXTERNAL:
81 platform_path = file_ref_host->GetExternalFilePath();
82 break;
83 default:
84 NOTREACHED();
86 http_body->appendFileRange(platform_path.AsUTF16Unsafe(),
87 start_offset,
88 number_of_bytes,
89 expected_last_modified_time);
90 return true;
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) {
101 return false;
103 return true;
106 std::string FilterStringForXRequestedWithValue(const std::string& s) {
107 std::string rv;
108 rv.reserve(s.length());
109 for (size_t i = 0; i < s.length(); i++) {
110 char c = s[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 == '_'))
115 rv.push_back(c);
117 return rv;
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
124 // version.
125 std::string MakeXRequestedWithValue(const std::string& name,
126 const std::string& version) {
127 std::string rv = FilterStringForXRequestedWithValue(name);
128 if (rv.empty())
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;
139 return rv;
142 } // namespace
144 bool CreateWebURLRequest(PP_Instance instance,
145 URLRequestInfoData* data,
146 WebFrame* frame,
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))
152 return false;
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);
160 if (instance_impl) {
161 name_version = MakeXRequestedWithValue(
162 instance_impl->module()->name(),
163 instance_impl->module()->version());
165 } else {
166 name_version = "internal_testing_only";
169 dest->initialize();
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();
192 int file_index = 0;
193 for (size_t i = 0; i < data->body.size(); ++i) {
194 const URLRequestInfoData::BodyItem& item = data->body[i];
195 if (item.is_file) {
196 if (!AppendFileRefToBody(instance,
197 item.file_ref_pp_resource,
198 item.start_offset,
199 item.number_of_bytes,
200 item.expected_last_modified_time,
201 &http_body))
202 return false;
203 file_index++;
204 } else {
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);
237 return true;
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