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 "ppapi/tests/test_url_loader.h"
6
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <string>
11
12 #include "ppapi/c/pp_errors.h"
13 #include "ppapi/c/ppb_file_io.h"
14 #include "ppapi/c/ppb_url_loader.h"
15 #include "ppapi/c/trusted/ppb_url_loader_trusted.h"
16 #include "ppapi/cpp/dev/url_util_dev.h"
17 #include "ppapi/cpp/file_io.h"
18 #include "ppapi/cpp/file_ref.h"
19 #include "ppapi/cpp/file_system.h"
20 #include "ppapi/cpp/instance.h"
21 #include "ppapi/cpp/module.h"
22 #include "ppapi/cpp/private/file_io_private.h"
23 #include "ppapi/cpp/url_loader.h"
24 #include "ppapi/cpp/url_request_info.h"
25 #include "ppapi/cpp/url_response_info.h"
26 #include "ppapi/tests/test_utils.h"
27 #include "ppapi/tests/testing_instance.h"
28
29 REGISTER_TEST_CASE(URLLoader);
30
31 namespace {
32
WriteEntireBuffer(PP_Instance instance,pp::FileIO * file_io,int32_t offset,const std::string & data,CallbackType callback_type)33 int32_t WriteEntireBuffer(PP_Instance instance,
34 pp::FileIO* file_io,
35 int32_t offset,
36 const std::string& data,
37 CallbackType callback_type) {
38 TestCompletionCallback callback(instance, callback_type);
39 int32_t write_offset = offset;
40 const char* buf = data.c_str();
41 int32_t size = static_cast<int32_t>(data.size());
42
43 while (write_offset < offset + size) {
44 callback.WaitForResult(file_io->Write(write_offset,
45 &buf[write_offset - offset],
46 size - write_offset + offset,
47 callback.GetCallback()));
48 if (callback.result() < 0)
49 return callback.result();
50 if (callback.result() == 0)
51 return PP_ERROR_FAILED;
52 write_offset += callback.result();
53 }
54
55 return PP_OK;
56 }
57
58 } // namespace
59
TestURLLoader(TestingInstance * instance)60 TestURLLoader::TestURLLoader(TestingInstance* instance)
61 : TestCase(instance),
62 file_io_private_interface_(NULL),
63 url_loader_trusted_interface_(NULL) {
64 }
65
Init()66 bool TestURLLoader::Init() {
67 if (!CheckTestingInterface()) {
68 instance_->AppendError("Testing interface not available");
69 return false;
70 }
71
72 const PPB_FileIO* file_io_interface = static_cast<const PPB_FileIO*>(
73 pp::Module::Get()->GetBrowserInterface(PPB_FILEIO_INTERFACE));
74 if (!file_io_interface)
75 instance_->AppendError("FileIO interface not available");
76
77 file_io_private_interface_ = static_cast<const PPB_FileIO_Private*>(
78 pp::Module::Get()->GetBrowserInterface(PPB_FILEIO_PRIVATE_INTERFACE));
79 if (!file_io_private_interface_)
80 instance_->AppendError("FileIO_Private interface not available");
81 url_loader_trusted_interface_ = static_cast<const PPB_URLLoaderTrusted*>(
82 pp::Module::Get()->GetBrowserInterface(PPB_URLLOADERTRUSTED_INTERFACE));
83 if (!testing_interface_->IsOutOfProcess()) {
84 // Trusted interfaces are not supported under NaCl.
85 #if !(defined __native_client__)
86 if (!url_loader_trusted_interface_)
87 instance_->AppendError("URLLoaderTrusted interface not available");
88 #else
89 if (url_loader_trusted_interface_)
90 instance_->AppendError("URLLoaderTrusted interface is supported by NaCl");
91 #endif
92 }
93 return EnsureRunningOverHTTP();
94 }
95
96 /*
97 * The test order is important here, as running tests out of order may cause
98 * test timeout.
99 *
100 * Here is the environment:
101 *
102 * 1. net::EmbeddedTestServer only accepts one open connection at the time.
103 * 2. HTTP socket pool keeps sockets open for several seconds after last use
104 * (hoping that there will be another request that could reuse the connection).
105 * 3. HTTP socket pool is separated by host/port and privacy mode (which is
106 * based on cookies set/get permissions). So, connections to 127.0.0.1,
107 * localhost and localhost in privacy mode cannot reuse existing socket and will
108 * try to open another connection.
109 *
110 * Here is the problem:
111 *
112 * Original test order was repeatedly accessing 127.0.0.1, localhost and
113 * localhost in privacy mode, causing new sockets to open and try to connect to
114 * testserver, which they couldn't until previous connection is closed by socket
115 * pool idle socket timeout (10 seconds).
116 *
117 * Because of this the test run was taking around 45 seconds, and test was
118 * reported as 'timed out' by trybot.
119 *
120 * Re-ordering of tests provides more sequential access to 127.0.0.1, localhost
121 * and localhost in privacy mode. It decreases the number of times when socket
122 * pool doesn't have existing connection to host and has to wait, therefore
123 * reducing total test time and ensuring its completion under 30 seconds.
124 */
RunTests(const std::string & filter)125 void TestURLLoader::RunTests(const std::string& filter) {
126 // These tests connect to 127.0.0.1:
127 RUN_CALLBACK_TEST(TestURLLoader, BasicGET, filter);
128 RUN_CALLBACK_TEST(TestURLLoader, BasicPOST, filter);
129 RUN_CALLBACK_TEST(TestURLLoader, BasicFilePOST, filter);
130 RUN_CALLBACK_TEST(TestURLLoader, BasicFileRangePOST, filter);
131 RUN_CALLBACK_TEST(TestURLLoader, CompoundBodyPOST, filter);
132 RUN_CALLBACK_TEST(TestURLLoader, EmptyDataPOST, filter);
133 RUN_CALLBACK_TEST(TestURLLoader, BinaryDataPOST, filter);
134 RUN_CALLBACK_TEST(TestURLLoader, CustomRequestHeader, filter);
135 RUN_CALLBACK_TEST(TestURLLoader, FailsBogusContentLength, filter);
136 RUN_CALLBACK_TEST(TestURLLoader, StreamToFile, filter);
137 RUN_CALLBACK_TEST(TestURLLoader, UntrustedJavascriptURLRestriction, filter);
138 RUN_CALLBACK_TEST(TestURLLoader, TrustedJavascriptURLRestriction, filter);
139 RUN_CALLBACK_TEST(TestURLLoader, UntrustedHttpRequests, filter);
140 RUN_CALLBACK_TEST(TestURLLoader, TrustedHttpRequests, filter);
141 RUN_CALLBACK_TEST(TestURLLoader, FollowURLRedirect, filter);
142 RUN_CALLBACK_TEST(TestURLLoader, AuditURLRedirect, filter);
143 RUN_CALLBACK_TEST(TestURLLoader, RestrictURLRedirectCommon, filter);
144 RUN_CALLBACK_TEST(TestURLLoader, RestrictURLRedirectEnabled, filter);
145 RUN_CALLBACK_TEST(TestURLLoader, RestrictURLRedirectDisabled, filter);
146 RUN_CALLBACK_TEST(TestURLLoader, AbortCalls, filter);
147 RUN_CALLBACK_TEST(TestURLLoader, UntendedLoad, filter);
148 RUN_CALLBACK_TEST(TestURLLoader, PrefetchBufferThreshold, filter);
149 RUN_CALLBACK_TEST(TestURLLoader, XRequestedWithHeader, filter);
150 // These tests connect to localhost with privacy mode enabled:
151 RUN_CALLBACK_TEST(TestURLLoader, UntrustedSameOriginRestriction, filter);
152 RUN_CALLBACK_TEST(TestURLLoader, UntrustedCrossOriginRequest, filter);
153 RUN_CALLBACK_TEST(TestURLLoader, UntrustedCorbEligibleRequest, filter);
154 // These tests connect to localhost with privacy mode disabled:
155 RUN_CALLBACK_TEST(TestURLLoader, TrustedSameOriginRestriction, filter);
156 RUN_CALLBACK_TEST(TestURLLoader, TrustedCrossOriginRequest, filter);
157 RUN_CALLBACK_TEST(TestURLLoader, TrustedCorbEligibleRequest, filter);
158 }
159
ReadEntireFile(pp::FileIO * file_io,std::string * data)160 std::string TestURLLoader::ReadEntireFile(pp::FileIO* file_io,
161 std::string* data) {
162 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
163 char buf[256];
164 int64_t offset = 0;
165
166 for (;;) {
167 callback.WaitForResult(file_io->Read(offset, buf, sizeof(buf),
168 callback.GetCallback()));
169 if (callback.result() < 0)
170 return ReportError("FileIO::Read", callback.result());
171 if (callback.result() == 0)
172 break;
173 offset += callback.result();
174 data->append(buf, callback.result());
175 }
176
177 PASS();
178 }
179
ReadEntireResponseBody(pp::URLLoader * loader,std::string * body)180 std::string TestURLLoader::ReadEntireResponseBody(pp::URLLoader* loader,
181 std::string* body) {
182 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
183 char buf[2]; // Small so that multiple reads are needed.
184
185 for (;;) {
186 callback.WaitForResult(
187 loader->ReadResponseBody(buf, sizeof(buf), callback.GetCallback()));
188 if (callback.result() < 0)
189 return ReportError("URLLoader::ReadResponseBody", callback.result());
190 if (callback.result() == 0)
191 break;
192 body->append(buf, callback.result());
193 }
194
195 PASS();
196 }
197
LoadAndCompareBody(const pp::URLRequestInfo & request,const std::string & expected_body)198 std::string TestURLLoader::LoadAndCompareBody(
199 const pp::URLRequestInfo& request,
200 const std::string& expected_body) {
201 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
202
203 pp::URLLoader loader(instance_);
204 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
205 CHECK_CALLBACK_BEHAVIOR(callback);
206 ASSERT_EQ(PP_OK, callback.result());
207
208 pp::URLResponseInfo response_info(loader.GetResponseInfo());
209 if (response_info.is_null())
210 return "URLLoader::GetResponseInfo returned null";
211 int32_t status_code = response_info.GetStatusCode();
212 if (status_code != 200)
213 return "Unexpected HTTP status code";
214
215 std::string body;
216 std::string error = ReadEntireResponseBody(&loader, &body);
217 if (!error.empty())
218 return error;
219
220 if (body.size() != expected_body.size())
221 return "URLLoader::ReadResponseBody returned unexpected content length";
222 if (body != expected_body)
223 return "URLLoader::ReadResponseBody returned unexpected content";
224
225 PASS();
226 }
227
LoadAndFail(const pp::URLRequestInfo & request)228 std::string TestURLLoader::LoadAndFail(const pp::URLRequestInfo& request) {
229 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
230
231 pp::URLLoader loader(instance_);
232 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
233 CHECK_CALLBACK_BEHAVIOR(callback);
234 ASSERT_EQ(PP_ERROR_FAILED, callback.result());
235
236 PASS();
237 }
238
OpenFileSystem(pp::FileSystem * file_system,std::string * message)239 int32_t TestURLLoader::OpenFileSystem(pp::FileSystem* file_system,
240 std::string* message) {
241 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
242 callback.WaitForResult(file_system->Open(1024, callback.GetCallback()));
243 if (callback.failed()) {
244 message->assign(callback.errors());
245 return callback.result();
246 }
247 if (callback.result() != PP_OK) {
248 message->assign("FileSystem::Open");
249 return callback.result();
250 }
251 return callback.result();
252 }
253
PrepareFileForPost(const pp::FileRef & file_ref,const std::string & data,std::string * message)254 int32_t TestURLLoader::PrepareFileForPost(
255 const pp::FileRef& file_ref,
256 const std::string& data,
257 std::string* message) {
258 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
259 pp::FileIO file_io(instance_);
260 callback.WaitForResult(file_io.Open(file_ref,
261 PP_FILEOPENFLAG_CREATE |
262 PP_FILEOPENFLAG_TRUNCATE |
263 PP_FILEOPENFLAG_WRITE,
264 callback.GetCallback()));
265 if (callback.failed()) {
266 message->assign(callback.errors());
267 return callback.result();
268 }
269 if (callback.result() != PP_OK) {
270 message->assign("FileIO::Open failed.");
271 return callback.result();
272 }
273
274 int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, data,
275 callback_type());
276 if (rv != PP_OK) {
277 message->assign("FileIO::Write failed.");
278 return rv;
279 }
280
281 return rv;
282 }
283
GetReachableAbsoluteURL(const std::string & file_name)284 std::string TestURLLoader::GetReachableAbsoluteURL(
285 const std::string& file_name) {
286 // Get the absolute page URL and replace the test case file name
287 // with the given one.
288 pp::Var document_url(
289 pp::PASS_REF,
290 testing_interface_->GetDocumentURL(instance_->pp_instance(),
291 NULL));
292 std::string url(document_url.AsString());
293 std::string old_name("test_case.html");
294 size_t index = url.find(old_name);
295 ASSERT_NE(index, std::string::npos);
296 url.replace(index, old_name.length(), file_name);
297 return url;
298 }
299
GetReachableCrossOriginURL(const std::string & file_name)300 std::string TestURLLoader::GetReachableCrossOriginURL(
301 const std::string& file_name) {
302 // Get an absolute URL and use it to construct a URL that will be
303 // considered cross-origin by the CORS access control code, and yet be
304 // reachable by the test server.
305 std::string url = GetReachableAbsoluteURL(file_name);
306 // Replace '127.0.0.1' with 'localhost'.
307 std::string host("127.0.0.1");
308 size_t index = url.find(host);
309 ASSERT_NE(index, std::string::npos);
310 url.replace(index, host.length(), "localhost");
311 return url;
312 }
313
OpenUntrusted(const std::string & method,const std::string & header)314 int32_t TestURLLoader::OpenUntrusted(const std::string& method,
315 const std::string& header) {
316 pp::URLRequestInfo request(instance_);
317 request.SetURL("/echo");
318 request.SetMethod(method);
319 request.SetHeaders(header);
320
321 return OpenUntrusted(request, NULL);
322 }
323
OpenTrusted(const std::string & method,const std::string & header)324 int32_t TestURLLoader::OpenTrusted(const std::string& method,
325 const std::string& header) {
326 pp::URLRequestInfo request(instance_);
327 request.SetURL("/echo");
328 request.SetMethod(method);
329 request.SetHeaders(header);
330
331 return OpenTrusted(request, NULL);
332 }
333
OpenUntrusted(const pp::URLRequestInfo & request,std::string * response_body)334 int32_t TestURLLoader::OpenUntrusted(const pp::URLRequestInfo& request,
335 std::string* response_body) {
336 return Open(request, false, response_body);
337 }
338
OpenTrusted(const pp::URLRequestInfo & request,std::string * response_body)339 int32_t TestURLLoader::OpenTrusted(const pp::URLRequestInfo& request,
340 std::string* response_body) {
341 return Open(request, true, response_body);
342 }
343
Open(const pp::URLRequestInfo & request,bool trusted,std::string * response_body)344 int32_t TestURLLoader::Open(const pp::URLRequestInfo& request,
345 bool trusted,
346 std::string* response_body) {
347 pp::URLLoader loader(instance_);
348 if (trusted)
349 url_loader_trusted_interface_->GrantUniversalAccess(loader.pp_resource());
350
351 return OpenURLRequest(instance_->pp_instance(), &loader, request,
352 callback_type(), response_body);
353 }
354
TestBasicGET()355 std::string TestURLLoader::TestBasicGET() {
356 pp::URLRequestInfo request(instance_);
357 request.SetURL("test_url_loader_data/hello.txt");
358 return LoadAndCompareBody(request, "hello\n");
359 }
360
TestBasicPOST()361 std::string TestURLLoader::TestBasicPOST() {
362 pp::URLRequestInfo request(instance_);
363 request.SetURL("/echo");
364 request.SetMethod("POST");
365 std::string postdata("postdata");
366 request.AppendDataToBody(postdata.data(),
367 static_cast<uint32_t>(postdata.length()));
368 return LoadAndCompareBody(request, postdata);
369 }
370
TestBasicFilePOST()371 std::string TestURLLoader::TestBasicFilePOST() {
372 std::string message;
373
374 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
375 int32_t rv = OpenFileSystem(&file_system, &message);
376 if (rv != PP_OK)
377 return ReportError(message.c_str(), rv);
378
379 pp::FileRef file_ref(file_system, "/file_post_test");
380 std::string postdata("postdata");
381 rv = PrepareFileForPost(file_ref, postdata, &message);
382 if (rv != PP_OK)
383 return ReportError(message.c_str(), rv);
384
385 pp::URLRequestInfo request(instance_);
386 request.SetURL("/echo");
387 request.SetMethod("POST");
388 request.AppendFileToBody(file_ref, 0);
389 return LoadAndCompareBody(request, postdata);
390 }
391
TestBasicFileRangePOST()392 std::string TestURLLoader::TestBasicFileRangePOST() {
393 std::string message;
394
395 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
396 int32_t rv = OpenFileSystem(&file_system, &message);
397 if (rv != PP_OK)
398 return ReportError(message.c_str(), rv);
399
400 pp::FileRef file_ref(file_system, "/file_range_post_test");
401 std::string postdata("postdatapostdata");
402 rv = PrepareFileForPost(file_ref, postdata, &message);
403 if (rv != PP_OK)
404 return ReportError(message.c_str(), rv);
405
406 pp::URLRequestInfo request(instance_);
407 request.SetURL("/echo");
408 request.SetMethod("POST");
409 request.AppendFileRangeToBody(file_ref, 4, 12, 0);
410 return LoadAndCompareBody(request, postdata.substr(4, 12));
411 }
412
TestCompoundBodyPOST()413 std::string TestURLLoader::TestCompoundBodyPOST() {
414 pp::URLRequestInfo request(instance_);
415 request.SetURL("/echo");
416 request.SetMethod("POST");
417 std::string postdata1("post");
418 request.AppendDataToBody(postdata1.data(),
419 static_cast<uint32_t>(postdata1.length()));
420 std::string postdata2("data");
421 request.AppendDataToBody(postdata2.data(),
422 static_cast<uint32_t>(postdata2.length()));
423 return LoadAndCompareBody(request, postdata1 + postdata2);
424 }
425
TestEmptyDataPOST()426 std::string TestURLLoader::TestEmptyDataPOST() {
427 pp::URLRequestInfo request(instance_);
428 request.SetURL("/echo");
429 request.SetMethod("POST");
430 request.AppendDataToBody("", 0);
431 return LoadAndCompareBody(request, std::string());
432 }
433
TestBinaryDataPOST()434 std::string TestURLLoader::TestBinaryDataPOST() {
435 pp::URLRequestInfo request(instance_);
436 request.SetURL("/echo");
437 request.SetMethod("POST");
438 const char postdata_chars[] =
439 "\x00\x01\x02\x03\x04\x05postdata\xfa\xfb\xfc\xfd\xfe\xff";
440 std::string postdata(postdata_chars,
441 sizeof(postdata_chars) / sizeof(postdata_chars[0]));
442 request.AppendDataToBody(postdata.data(),
443 static_cast<uint32_t>(postdata.length()));
444 return LoadAndCompareBody(request, postdata);
445 }
446
TestCustomRequestHeader()447 std::string TestURLLoader::TestCustomRequestHeader() {
448 pp::URLRequestInfo request(instance_);
449 request.SetURL("/echoheader?Foo");
450 request.SetHeaders("Foo: 1");
451 return LoadAndCompareBody(request, "1");
452 }
453
TestFailsBogusContentLength()454 std::string TestURLLoader::TestFailsBogusContentLength() {
455 pp::URLRequestInfo request(instance_);
456 request.SetURL("/echo");
457 request.SetMethod("POST");
458 request.SetHeaders("Content-Length: 400");
459 std::string postdata("postdata");
460 request.AppendDataToBody(postdata.data(),
461 static_cast<uint32_t>(postdata.length()));
462
463 int32_t rv;
464 rv = OpenUntrusted(request, NULL);
465 if (rv != PP_ERROR_NOACCESS)
466 return ReportError(
467 "Untrusted request with bogus Content-Length restriction", rv);
468
469 PASS();
470 }
471
TestStreamToFile()472 std::string TestURLLoader::TestStreamToFile() {
473 pp::URLRequestInfo request(instance_);
474 request.SetURL("test_url_loader_data/hello.txt");
475 ASSERT_FALSE(request.SetStreamToFile(true));
476
477 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
478
479 pp::URLLoader loader(instance_);
480 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
481 CHECK_CALLBACK_BEHAVIOR(callback);
482 ASSERT_EQ(PP_OK, callback.result());
483
484 pp::URLResponseInfo response_info(loader.GetResponseInfo());
485 if (response_info.is_null())
486 return "URLLoader::GetResponseInfo returned null";
487 int32_t status_code = response_info.GetStatusCode();
488 if (status_code != 200)
489 return "Unexpected HTTP status code";
490
491 pp::FileRef body(response_info.GetBodyAsFileRef());
492 ASSERT_TRUE(body.is_null());
493
494 callback.WaitForResult(loader.FinishStreamingToFile(callback.GetCallback()));
495 CHECK_CALLBACK_BEHAVIOR(callback);
496 ASSERT_EQ(PP_ERROR_NOTSUPPORTED, callback.result());
497
498 PASS();
499 }
500
501 // Untrusted, unintended cross-origin requests should fail.
TestUntrustedSameOriginRestriction()502 std::string TestURLLoader::TestUntrustedSameOriginRestriction() {
503 pp::URLRequestInfo request(instance_);
504 std::string cross_origin_url = GetReachableCrossOriginURL("test_case.html");
505 request.SetURL(cross_origin_url);
506
507 int32_t rv = OpenUntrusted(request, NULL);
508 if (rv != PP_ERROR_NOACCESS)
509 return ReportError(
510 "Untrusted, unintended cross-origin request restriction", rv);
511
512 PASS();
513 }
514
515 // Trusted, unintended cross-origin requests should succeed.
TestTrustedSameOriginRestriction()516 std::string TestURLLoader::TestTrustedSameOriginRestriction() {
517 pp::URLRequestInfo request(instance_);
518 std::string cross_origin_url = GetReachableCrossOriginURL("test_case.html");
519 request.SetURL(cross_origin_url);
520
521 int32_t rv = OpenTrusted(request, NULL);
522 if (rv != PP_OK)
523 return ReportError("Trusted cross-origin request failed", rv);
524
525 PASS();
526 }
527
528 // Untrusted, intended cross-origin requests should use CORS and succeed.
TestUntrustedCrossOriginRequest()529 std::string TestURLLoader::TestUntrustedCrossOriginRequest() {
530 pp::URLRequestInfo request(instance_);
531 std::string cross_origin_url = GetReachableCrossOriginURL("test_case.html");
532 request.SetURL(cross_origin_url);
533 request.SetAllowCrossOriginRequests(true);
534
535 int32_t rv = OpenUntrusted(request, NULL);
536 if (rv != PP_OK)
537 return ReportError(
538 "Untrusted, intended cross-origin request failed", rv);
539
540 PASS();
541 }
542
543 // Trusted, intended cross-origin requests should use CORS and succeed.
TestTrustedCrossOriginRequest()544 std::string TestURLLoader::TestTrustedCrossOriginRequest() {
545 pp::URLRequestInfo request(instance_);
546 std::string cross_origin_url = GetReachableCrossOriginURL("test_case.html");
547 request.SetURL(cross_origin_url);
548 request.SetAllowCrossOriginRequests(true);
549
550 int32_t rv = OpenTrusted(request, NULL);
551 if (rv != PP_OK)
552 return ReportError("Trusted cross-origin request failed", rv);
553
554 PASS();
555 }
556
557 // CORB (Cross-Origin Read Blocking) should apply to plugins without universal
558 // access. This test is very similar to TestUntrustedSameOriginRestriction, but
559 // explicitly uses a CORB-eligible response (test/json + nosniff).
TestUntrustedCorbEligibleRequest()560 std::string TestURLLoader::TestUntrustedCorbEligibleRequest() {
561 // It is important to use a CORB-eligible response here: text/json + nosniff.
562 std::string cross_origin_url =
563 GetReachableCrossOriginURL("corb_eligible_resource.json");
564
565 pp::URLRequestInfo request(instance_);
566 request.SetURL(cross_origin_url);
567 request.SetAllowCrossOriginRequests(true);
568
569 std::string response_body;
570 int32_t rv = OpenUntrusted(request, &response_body);
571
572 // Main verification - the response should be blocked. Ideally the blocking
573 // should be done before the data leaves the browser and/or network-service
574 // process (the test doesn't verify this though).
575 if (rv != PP_ERROR_NOACCESS) {
576 return ReportError("Untrusted Javascript URL request restriction failed",
577 rv);
578 }
579 ASSERT_EQ("", response_body);
580 PASS();
581 }
582
583 // CORB (Cross-Origin Read Blocking) shouldn't apply to plugins with universal
584 // access (see PepperURLLoaderHost::has_universal_access_) - such plugins may
585 // have their own CORS-like mechanisms - e.g. crossdomain.xml in Flash).
586 // This test is quite similar to TestTrustedSameOriginRestriction, but it
587 // explicitly uses a CORB-eligible response (test/json + nosniff) and also
588 // explicitly verifies that the response body was not blocked.
TestTrustedCorbEligibleRequest()589 std::string TestURLLoader::TestTrustedCorbEligibleRequest() {
590 // It is important to use a CORB-eligible response here: text/json + nosniff.
591 std::string cross_origin_url =
592 GetReachableCrossOriginURL("corb_eligible_resource.json");
593
594 pp::URLRequestInfo request(instance_);
595 request.SetURL(cross_origin_url);
596 request.SetAllowCrossOriginRequests(true);
597
598 std::string response_body;
599 int32_t rv = OpenTrusted(request, &response_body);
600 if (rv != PP_OK)
601 return ReportError("Trusted CORB-eligible request failed", rv);
602
603 // Main verification - if CORB blocked the response, then |response_body|
604 // would be empty.
605 ASSERT_EQ("{ \"foo\": \"bar\" }\n", response_body);
606 PASS();
607 }
608
609 // Untrusted Javascript URLs requests should fail.
TestUntrustedJavascriptURLRestriction()610 std::string TestURLLoader::TestUntrustedJavascriptURLRestriction() {
611 pp::URLRequestInfo request(instance_);
612 request.SetURL("javascript:foo = bar");
613
614 int32_t rv = OpenUntrusted(request, NULL);
615 if (rv != PP_ERROR_NOACCESS)
616 return ReportError(
617 "Untrusted Javascript URL request restriction failed", rv);
618
619 PASS();
620 }
621
622 // Trusted Javascript URLs requests should succeed.
TestTrustedJavascriptURLRestriction()623 std::string TestURLLoader::TestTrustedJavascriptURLRestriction() {
624 pp::URLRequestInfo request(instance_);
625 request.SetURL("javascript:foo = bar");
626
627 int32_t rv = OpenTrusted(request, NULL);
628 if (rv == PP_ERROR_NOACCESS)
629 return ReportError(
630 "Trusted Javascript URL request", rv);
631
632 PASS();
633 }
634
TestUntrustedHttpRequests()635 std::string TestURLLoader::TestUntrustedHttpRequests() {
636 // HTTP methods are restricted only for untrusted loaders. Forbidden
637 // methods are CONNECT, TRACE, and TRACK, and any string that is not a
638 // valid token (containing special characters like CR, LF).
639 // http://www.w3.org/TR/XMLHttpRequest/
640 {
641 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("cOnNeCt", std::string()));
642 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("tRaCk", std::string()));
643 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("tRaCe", std::string()));
644 ASSERT_EQ(PP_ERROR_NOACCESS,
645 OpenUntrusted("POST\x0d\x0ax-csrf-token:\x20test1234", std::string()));
646 }
647 // HTTP methods are restricted only for untrusted loaders. Try all headers
648 // that are forbidden by http://www.w3.org/TR/XMLHttpRequest/.
649 {
650 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Accept-Charset:\n"));
651 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Accept-Encoding:\n"));
652 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Connection:\n"));
653 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Content-Length:\n"));
654 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Cookie:\n"));
655 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Cookie2:\n"));
656 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Date:\n"));
657 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Dnt:\n"));
658 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Expect:\n"));
659 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Host:\n"));
660 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Keep-Alive:\n"));
661 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Referer:\n"));
662 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "TE:\n"));
663 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Trailer:\n"));
664 ASSERT_EQ(PP_ERROR_NOACCESS,
665 OpenUntrusted("GET", "Transfer-Encoding:\n"));
666 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Upgrade:\n"));
667 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "User-Agent:\n"));
668 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Via:\n"));
669 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted(
670 "GET", "Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==:\n"));
671 ASSERT_EQ(PP_ERROR_NOACCESS, OpenUntrusted("GET", "Sec-foo:\n"));
672 }
673 // Untrusted requests with custom referrer should fail.
674 {
675 pp::URLRequestInfo request(instance_);
676 request.SetCustomReferrerURL("http://www.google.com/");
677
678 int32_t rv = OpenUntrusted(request, NULL);
679 if (rv != PP_ERROR_NOACCESS)
680 return ReportError(
681 "Untrusted request with custom referrer restriction", rv);
682 }
683 // Untrusted requests with custom transfer encodings should fail.
684 {
685 pp::URLRequestInfo request(instance_);
686 request.SetCustomContentTransferEncoding("foo");
687
688 int32_t rv = OpenUntrusted(request, NULL);
689 if (rv != PP_ERROR_NOACCESS)
690 return ReportError(
691 "Untrusted request with content-transfer-encoding restriction", rv);
692 }
693
694 PASS();
695 }
696
TestTrustedHttpRequests()697 std::string TestURLLoader::TestTrustedHttpRequests() {
698 // Trusted requests can use restricted methods.
699 {
700 ASSERT_EQ(PP_OK, OpenTrusted("cOnNeCt", std::string()));
701 ASSERT_EQ(PP_OK, OpenTrusted("tRaCk", std::string()));
702 ASSERT_EQ(PP_OK, OpenTrusted("tRaCe", std::string()));
703 }
704 // Trusted requests can use restricted headers.
705 {
706 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Accept-Charset:\n"));
707 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Accept-Encoding:\n"));
708 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Connection:\n"));
709 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Cookie:\n"));
710 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Date:\n"));
711 ASSERT_EQ(PP_OK, OpenTrusted("GET", "DNT:\n"));
712 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Expect:\n"));
713
714 // Host header is still forbidden because it can conflict with specific URL.
715
716 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Referer:\n"));
717 ASSERT_EQ(PP_OK, OpenTrusted("GET", "User-Agent:\n"));
718 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Via:\n"));
719 ASSERT_EQ(PP_OK, OpenTrusted("GET", "Sec-foo:\n"));
720 }
721 // Trusted requests with custom referrer should succeed.
722 {
723 pp::URLRequestInfo request(instance_);
724 request.SetCustomReferrerURL("http://www.referer.com/");
725 request.SetHeaders("Referer: http://www.referer.com/");
726
727 int32_t rv = OpenTrusted(request, NULL);
728 if (rv != PP_OK)
729 return ReportError("Trusted request with custom referrer", rv);
730 }
731 // Trusted requests with custom transfer encodings should succeed.
732 {
733 pp::URLRequestInfo request(instance_);
734 request.SetCustomContentTransferEncoding("foo");
735
736 int32_t rv = OpenTrusted(request, NULL);
737 if (rv != PP_OK)
738 return ReportError(
739 "Trusted request with content-transfer-encoding failed", rv);
740 }
741
742 PASS();
743 }
744
745 // This test should cause a redirect and ensure that the loader follows it.
TestFollowURLRedirect()746 std::string TestURLLoader::TestFollowURLRedirect() {
747 pp::URLRequestInfo request(instance_);
748 // This prefix causes the test server to return a 301 redirect.
749 std::string redirect_prefix("/server-redirect?");
750 // We need an absolute path for the redirect to actually work.
751 std::string redirect_url =
752 GetReachableAbsoluteURL("test_url_loader_data/hello.txt");
753 request.SetURL(redirect_prefix.append(redirect_url));
754 return LoadAndCompareBody(request, "hello\n");
755 }
756
757 // This test should cause a redirect and ensure that the loader runs
758 // the callback, rather than following the redirect.
TestAuditURLRedirect()759 std::string TestURLLoader::TestAuditURLRedirect() {
760 pp::URLRequestInfo request(instance_);
761 // This path will cause the server to return a 301 redirect.
762 // This prefix causes the test server to return a 301 redirect.
763 std::string redirect_prefix("/server-redirect?");
764 // We need an absolute path for the redirect to actually work.
765 std::string redirect_url =
766 GetReachableAbsoluteURL("test_url_loader_data/hello.txt");
767 request.SetURL(redirect_prefix.append(redirect_url));
768 request.SetFollowRedirects(false);
769
770 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
771
772 pp::URLLoader loader(instance_);
773 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
774 CHECK_CALLBACK_BEHAVIOR(callback);
775 ASSERT_EQ(PP_OK, callback.result());
776
777 // Checks that the response indicates a redirect, and that the URL
778 // is correct.
779 pp::URLResponseInfo response_info(loader.GetResponseInfo());
780 if (response_info.is_null())
781 return "URLLoader::GetResponseInfo returned null";
782 int32_t status_code = response_info.GetStatusCode();
783 if (status_code != 301)
784 return "Response status should be 301";
785
786 // Test that the paused loader can be resumed.
787 callback.WaitForResult(loader.FollowRedirect(callback.GetCallback()));
788 CHECK_CALLBACK_BEHAVIOR(callback);
789 ASSERT_EQ(PP_OK, callback.result());
790 std::string body;
791 std::string error = ReadEntireResponseBody(&loader, &body);
792 if (!error.empty())
793 return error;
794
795 if (body != "hello\n")
796 return "URLLoader::FollowRedirect failed";
797
798 PASS();
799 }
800
801 // This test checks if the redirect restriction does not block acceptable cases
802 // of 307/308 GET and HEAD.
TestRestrictURLRedirectCommon()803 std::string TestURLLoader::TestRestrictURLRedirectCommon() {
804 std::string url = GetReachableAbsoluteURL("test_url_loader_data/hello.txt");
805 std::string redirect_307_prefix("/server-redirect-307?");
806
807 {
808 // Default method is GET and will follow the redirect.
809 pp::URLRequestInfo request_for_default_307(instance_);
810 request_for_default_307.SetURL(redirect_307_prefix.append(url));
811 std::string result_for_default_307 =
812 LoadAndCompareBody(request_for_default_307, "hello\n");
813 if (!result_for_default_307.empty())
814 return result_for_default_307;
815 }
816
817 {
818 // GET will follow the redirect.
819 pp::URLRequestInfo request_for_get_307(instance_);
820 request_for_get_307.SetURL(redirect_307_prefix.append(url));
821 request_for_get_307.SetMethod("GET");
822 std::string result_for_get_307 =
823 LoadAndCompareBody(request_for_get_307, "hello\n");
824 if (!result_for_get_307.empty())
825 return result_for_get_307;
826 }
827
828 {
829 // HEAD will follow the redirect.
830 pp::URLRequestInfo request_for_head_307(instance_);
831 request_for_head_307.SetURL(redirect_307_prefix.append(url));
832 request_for_head_307.SetMethod("HEAD");
833 std::string result_for_head_307 =
834 LoadAndCompareBody(request_for_head_307, "");
835 if (!result_for_head_307.empty())
836 return result_for_head_307;
837 }
838
839 std::string redirect_308_prefix("/server-redirect-308?");
840 {
841 // Default method is GET and will follow the redirect.
842 pp::URLRequestInfo request_for_default_308(instance_);
843 request_for_default_308.SetURL(redirect_308_prefix.append(url));
844 std::string result_for_default_308 =
845 LoadAndCompareBody(request_for_default_308, "hello\n");
846 if (!result_for_default_308.empty())
847 return result_for_default_308;
848 }
849
850 {
851 // GET will follow the redirect.
852 pp::URLRequestInfo request_for_get_308(instance_);
853 request_for_get_308.SetURL(redirect_308_prefix.append(url));
854 request_for_get_308.SetMethod("GET");
855 std::string result_for_get_308 =
856 LoadAndCompareBody(request_for_get_308, "hello\n");
857 if (!result_for_get_308.empty())
858 return result_for_get_308;
859 }
860
861 {
862 // HEAD will follow the redirect.
863 pp::URLRequestInfo request_for_head_308(instance_);
864 request_for_head_308.SetURL(redirect_308_prefix.append(url));
865 request_for_head_308.SetMethod("HEAD");
866 std::string result_for_head_308 =
867 LoadAndCompareBody(request_for_head_308, "");
868 if (!result_for_head_308.empty())
869 return result_for_head_308;
870 }
871
872 PASS();
873 }
874
875 // This test checks if the redirect restriction blocks the restricted cases of
876 // 307/308 POST.
TestRestrictURLRedirectEnabled()877 std::string TestURLLoader::TestRestrictURLRedirectEnabled() {
878 std::string url = GetReachableAbsoluteURL("test_url_loader_data/hello.txt");
879
880 {
881 // POST will be blocked and fail.
882 std::string redirect_307_prefix("/server-redirect-307?");
883 pp::URLRequestInfo request_for_post_307(instance_);
884 request_for_post_307.SetURL(redirect_307_prefix.append(url));
885 request_for_post_307.SetMethod("POST");
886 std::string result_for_post_307 = LoadAndFail(request_for_post_307);
887 if (!result_for_post_307.empty())
888 return result_for_post_307;
889 }
890
891 {
892 // POST will be blocked and fail.
893 pp::URLRequestInfo request_for_post_308(instance_);
894 std::string redirect_308_prefix("/server-redirect-308?");
895 request_for_post_308.SetURL(redirect_308_prefix.append(url));
896 request_for_post_308.SetMethod("POST");
897 std::string result_for_post_308 = LoadAndFail(request_for_post_308);
898 if (!result_for_post_308.empty())
899 return result_for_post_308;
900 }
901
902 PASS();
903 }
904
905 // This test checks if the redirect restriction does not block the restricted
906 // cases if the restriction is disabled.
TestRestrictURLRedirectDisabled()907 std::string TestURLLoader::TestRestrictURLRedirectDisabled() {
908 std::string url = GetReachableAbsoluteURL("test_url_loader_data/hello.txt");
909
910 {
911 // POST will not be blocked, but follow the redirect.
912 std::string redirect_307_prefix("/server-redirect-307?");
913 pp::URLRequestInfo request_for_post_307(instance_);
914 request_for_post_307.SetURL(redirect_307_prefix.append(url));
915 request_for_post_307.SetMethod("POST");
916 std::string result_for_post_307 =
917 LoadAndCompareBody(request_for_post_307, "hello\n");
918 if (!result_for_post_307.empty())
919 return result_for_post_307;
920 }
921
922 {
923 // POST will not be blocked, but follow the redirect.
924 pp::URLRequestInfo request_for_post_308(instance_);
925 std::string redirect_308_prefix("/server-redirect-308?");
926 request_for_post_308.SetURL(redirect_308_prefix.append(url));
927 request_for_post_308.SetMethod("POST");
928 std::string result_for_post_308 =
929 LoadAndCompareBody(request_for_post_308, "hello\n");
930 if (!result_for_post_308.empty())
931 return result_for_post_308;
932 }
933
934 PASS();
935 }
936
TestAbortCalls()937 std::string TestURLLoader::TestAbortCalls() {
938 pp::URLRequestInfo request(instance_);
939 request.SetURL("test_url_loader_data/hello.txt");
940
941 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
942 int32_t rv;
943
944 // Abort |Open()|.
945 {
946 rv = pp::URLLoader(instance_).Open(request, callback.GetCallback());
947 }
948 callback.WaitForAbortResult(rv);
949 CHECK_CALLBACK_BEHAVIOR(callback);
950
951 // Abort |ReadResponseBody()|.
952 {
953 char buf[2] = { 0 };
954 {
955 pp::URLLoader loader(instance_);
956 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
957 CHECK_CALLBACK_BEHAVIOR(callback);
958 ASSERT_EQ(PP_OK, callback.result());
959
960 rv = loader.ReadResponseBody(buf, sizeof(buf), callback.GetCallback());
961 } // Destroy |loader|.
962 callback.WaitForAbortResult(rv);
963 CHECK_CALLBACK_BEHAVIOR(callback);
964 if (rv == PP_OK_COMPLETIONPENDING) {
965 if (buf[0] || buf[1]) {
966 return "URLLoader::ReadResponseBody wrote data after resource "
967 "destruction.";
968 }
969 }
970 }
971
972 // TODO(viettrungluu): More abort tests (but add basic tests first).
973 // Also test that Close() aborts properly. crbug.com/69457
974
975 PASS();
976 }
977
TestUntendedLoad()978 std::string TestURLLoader::TestUntendedLoad() {
979 pp::URLRequestInfo request(instance_);
980 request.SetURL("test_url_loader_data/hello.txt");
981 request.SetRecordDownloadProgress(true);
982 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
983
984 pp::URLLoader loader(instance_);
985 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
986 CHECK_CALLBACK_BEHAVIOR(callback);
987 ASSERT_EQ(PP_OK, callback.result());
988
989 // We received the response callback. Yield until the network code has called
990 // the loader's didReceiveData and didFinishLoading methods before we give it
991 // another callback function, to make sure the loader works with no callback.
992 int64_t bytes_received = 0;
993 int64_t total_bytes_to_be_received = 0;
994 while (true) {
995 loader.GetDownloadProgress(&bytes_received, &total_bytes_to_be_received);
996 if (total_bytes_to_be_received <= 0)
997 return ReportError("URLLoader::GetDownloadProgress total size",
998 static_cast<int32_t>(total_bytes_to_be_received));
999 if (bytes_received == total_bytes_to_be_received)
1000 break;
1001 // Yield if we're on the main thread, so that URLLoader can receive more
1002 // data.
1003 if (pp::Module::Get()->core()->IsMainThread()) {
1004 NestedEvent event(instance_->pp_instance());
1005 event.PostSignal(10);
1006 event.Wait();
1007 }
1008 }
1009 // The loader should now have the data and have finished successfully.
1010 std::string body;
1011 std::string error = ReadEntireResponseBody(&loader, &body);
1012 if (!error.empty())
1013 return error;
1014 if (body != "hello\n")
1015 return ReportError("Couldn't read data", callback.result());
1016
1017 PASS();
1018 }
1019
OpenWithPrefetchBufferThreshold(int32_t lower,int32_t upper)1020 int32_t TestURLLoader::OpenWithPrefetchBufferThreshold(int32_t lower,
1021 int32_t upper) {
1022 pp::URLRequestInfo request(instance_);
1023 request.SetURL("test_url_loader_data/hello.txt");
1024 request.SetPrefetchBufferLowerThreshold(lower);
1025 request.SetPrefetchBufferUpperThreshold(upper);
1026
1027 return OpenUntrusted(request, NULL);
1028 }
1029
TestPrefetchBufferThreshold()1030 std::string TestURLLoader::TestPrefetchBufferThreshold() {
1031 int32_t rv = OpenWithPrefetchBufferThreshold(-1, 1);
1032 if (rv != PP_ERROR_FAILED) {
1033 return ReportError("The prefetch limits contained a negative value but "
1034 "the URLLoader did not fail.", rv);
1035 }
1036
1037 rv = OpenWithPrefetchBufferThreshold(0, 1);
1038 if (rv != PP_OK) {
1039 return ReportError("The prefetch buffer limits were legal values but "
1040 "the URLLoader failed.", rv);
1041 }
1042
1043 rv = OpenWithPrefetchBufferThreshold(1000, 1);
1044 if (rv != PP_ERROR_FAILED) {
1045 return ReportError("The lower buffer value was higher than the upper but "
1046 "the URLLoader did not fail.", rv);
1047 }
1048
1049 PASS();
1050 }
1051
1052 // TODO(viettrungluu): This test properly belongs elsewhere. It tests that
1053 // Chrome properly tags URL requests made on behalf of Pepper plugins (with an
1054 // X-Requested-With header), but this isn't, strictly speaking, a PPAPI
1055 // behavior.
TestXRequestedWithHeader()1056 std::string TestURLLoader::TestXRequestedWithHeader() {
1057 pp::URLRequestInfo request(instance_);
1058 request.SetURL("/echoheader?X-Requested-With");
1059 // The name and version of the plugin is set from the command-line (see
1060 // chrome/test/ppapi/ppapi_test.cc.
1061 return LoadAndCompareBody(request, "PPAPITests/1.2.3");
1062 }
1063
1064 // TODO(viettrungluu): Add tests for Get{Upload,Download}Progress, Close
1065 // (including abort tests if applicable).
1066