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 // Tests PPB_URLRequestInfo interface. 6 7 #include "ppapi/tests/test_url_request.h" 8 9 #include <string.h> 10 #include <string> 11 12 #include "ppapi/c/ppb_file_io.h" 13 #include "ppapi/cpp/completion_callback.h" 14 #include "ppapi/cpp/file_io.h" 15 #include "ppapi/cpp/file_ref.h" 16 #include "ppapi/cpp/file_system.h" 17 #include "ppapi/cpp/instance.h" 18 #include "ppapi/cpp/var.h" 19 #include "ppapi/tests/test_utils.h" 20 #include "ppapi/tests/testing_instance.h" 21 22 REGISTER_TEST_CASE(URLRequest); 23 24 namespace { 25 // TODO(polina): move these to test_case.h/cc since other NaCl tests use them? 26 27 const PP_Resource kInvalidResource = 0; 28 const PP_Instance kInvalidInstance = 0; 29 30 // These should not exist. 31 // The bottom 2 bits are used to differentiate between different id types. 32 // 00 - module, 01 - instance, 10 - resource, 11 - var. 33 const PP_Instance kNotAnInstance = 0xFFFFF0; 34 const PP_Resource kNotAResource = 0xAAAAA0; 35 } 36 37 TestURLRequest::TestURLRequest(TestingInstance* instance) 38 : TestCase(instance), 39 ppb_url_request_interface_(NULL), 40 ppb_url_loader_interface_(NULL), 41 ppb_url_response_interface_(NULL), 42 ppb_core_interface_(NULL), 43 ppb_var_interface_(NULL) { 44 } 45 46 bool TestURLRequest::Init() { 47 ppb_url_request_interface_ = static_cast<const PPB_URLRequestInfo*>( 48 pp::Module::Get()->GetBrowserInterface(PPB_URLREQUESTINFO_INTERFACE)); 49 ppb_url_loader_interface_ = static_cast<const PPB_URLLoader*>( 50 pp::Module::Get()->GetBrowserInterface(PPB_URLLOADER_INTERFACE)); 51 ppb_url_response_interface_ = static_cast<const PPB_URLResponseInfo*>( 52 pp::Module::Get()->GetBrowserInterface(PPB_URLRESPONSEINFO_INTERFACE)); 53 ppb_core_interface_ = static_cast<const PPB_Core*>( 54 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); 55 ppb_var_interface_ = static_cast<const PPB_Var*>( 56 pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); 57 if (!ppb_url_request_interface_) 58 instance_->AppendError("PPB_URLRequestInfo interface not available"); 59 if (!ppb_url_response_interface_) 60 instance_->AppendError("PPB_URLResponseInfo interface not available"); 61 if (!ppb_core_interface_) 62 instance_->AppendError("PPB_Core interface not available"); 63 if (!ppb_var_interface_) 64 instance_->AppendError("PPB_Var interface not available"); 65 if (!ppb_url_loader_interface_) { 66 instance_->AppendError("PPB_URLLoader interface not available"); 67 } 68 return EnsureRunningOverHTTP(); 69 } 70 71 void TestURLRequest::RunTests(const std::string& filter) { 72 RUN_TEST(CreateAndIsURLRequestInfo, filter); 73 RUN_TEST(SetProperty, filter); 74 RUN_TEST(AppendDataToBody, filter); 75 RUN_TEST(AppendFileToBody, filter); 76 RUN_TEST(Stress, filter); 77 } 78 79 PP_Var TestURLRequest::PP_MakeString(const char* s) { 80 return ppb_var_interface_->VarFromUtf8(s, strlen(s)); 81 } 82 83 // Tests 84 // PP_Resource Create(PP_Instance instance) 85 // PP_Bool IsURLRequestInfo(PP_Resource resource) 86 std::string TestURLRequest::TestCreateAndIsURLRequestInfo() { 87 // Create: Invalid / non-existent instance -> invalid resource. 88 ASSERT_EQ(ppb_url_request_interface_->Create(kInvalidInstance), 89 kInvalidResource); 90 ASSERT_EQ(ppb_url_request_interface_->Create(kNotAnInstance), 91 kInvalidResource); 92 93 // Create: Valid instance -> valid resource. 94 PP_Resource url_request = ppb_url_request_interface_->Create( 95 instance_->pp_instance()); 96 ASSERT_NE(url_request, kInvalidResource); 97 98 // IsURLRequestInfo: 99 // Invalid / non-existent / non-URLRequestInfo resource -> false. 100 ASSERT_NE(PP_TRUE, 101 ppb_url_request_interface_->IsURLRequestInfo(kInvalidResource)); 102 ASSERT_NE(PP_TRUE, 103 ppb_url_request_interface_->IsURLRequestInfo(kNotAResource)); 104 105 PP_Resource url_loader = 106 ppb_url_loader_interface_->Create(instance_->pp_instance()); 107 ASSERT_NE(kInvalidResource, url_loader); 108 109 ASSERT_NE(PP_TRUE, ppb_url_request_interface_->IsURLRequestInfo(url_loader)); 110 ppb_url_loader_interface_->Close(url_loader); 111 ppb_core_interface_->ReleaseResource(url_loader); 112 113 // IsURLRequestInfo: Current URLRequestInfo resource -> true. 114 std::string error; 115 if (PP_FALSE == ppb_url_request_interface_->IsURLRequestInfo(url_request)) 116 error = "IsURLRequestInfo() failed with a current URLRequestInfo resource"; 117 118 // IsURLRequestInfo: Released URLRequestInfo resource -> false. 119 ppb_core_interface_->ReleaseResource(url_request); 120 ASSERT_NE(PP_TRUE, ppb_url_request_interface_->IsURLRequestInfo(url_request)); 121 122 return error; // == PASS() if empty. 123 } 124 125 // Tests 126 // PP_Bool SetProperty(PP_Resource request, 127 // PP_URLRequestProperty property, 128 // struct PP_Var value); 129 std::string TestURLRequest::TestSetProperty() { 130 struct PropertyTestData { 131 PropertyTestData(PP_URLRequestProperty prop, 132 const std::string& name, 133 PP_Var value, PP_Bool expected) : 134 property(prop), property_name(name), 135 var(value), expected_value(expected) { 136 // var has ref count of 1 on creation. 137 } 138 PP_URLRequestProperty property; 139 std::string property_name; 140 PP_Var var; // Instance owner is responsible for releasing this var. 141 PP_Bool expected_value; 142 }; 143 144 // All bool properties should accept PP_TRUE and PP_FALSE, while rejecting 145 // all other variable types. 146 #define TEST_BOOL(_name) \ 147 PropertyTestData(ID_STR(_name), PP_MakeBool(PP_TRUE), PP_TRUE), \ 148 PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_TRUE), \ 149 PropertyTestData(ID_STR(_name), PP_MakeUndefined(), PP_FALSE), \ 150 PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE), \ 151 PropertyTestData(ID_STR(_name), PP_MakeInt32(0), PP_FALSE), \ 152 PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE) 153 154 // These property types are always invalid for string properties. 155 #define TEST_STRING_INVALID(_name) \ 156 PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE), \ 157 PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_FALSE), \ 158 PropertyTestData(ID_STR(_name), PP_MakeInt32(0), PP_FALSE), \ 159 PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE) 160 161 #define TEST_INT_INVALID(_name) \ 162 PropertyTestData(ID_STR(_name), PP_MakeUndefined(), PP_FALSE), \ 163 PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE), \ 164 PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_FALSE), \ 165 PropertyTestData(ID_STR(_name), PP_MakeString("notint"), PP_FALSE), \ 166 PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE) 167 168 // SetProperty accepts plenty of invalid values (malformed urls, negative 169 // thresholds, etc). Error checking is delayed until request opening (aka url 170 // loading). 171 #define ID_STR(arg) arg, #arg 172 PropertyTestData test_data[] = { 173 TEST_BOOL(PP_URLREQUESTPROPERTY_STREAMTOFILE), 174 TEST_BOOL(PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS), 175 TEST_BOOL(PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS), 176 TEST_BOOL(PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS), 177 TEST_BOOL(PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS), 178 TEST_BOOL(PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS), 179 TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_URL), 180 TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_METHOD), 181 TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_HEADERS), 182 TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL), 183 TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING), 184 TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT), 185 TEST_INT_INVALID(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD), 186 TEST_INT_INVALID(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD), 187 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL), 188 PP_MakeString("http://www.google.com"), PP_TRUE), 189 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL), 190 PP_MakeString("foo.jpg"), PP_TRUE), 191 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD), 192 PP_MakeString("GET"), PP_TRUE), 193 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD), 194 PP_MakeString("POST"), PP_TRUE), 195 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_HEADERS), 196 PP_MakeString("Accept: text/plain"), PP_TRUE), 197 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_HEADERS), 198 PP_MakeString(""), PP_TRUE), 199 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL), 200 PP_MakeString("http://www.google.com"), PP_TRUE), 201 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL), 202 PP_MakeString(""), PP_TRUE), 203 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL), 204 PP_MakeUndefined(), PP_TRUE), 205 PropertyTestData( 206 ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING), 207 PP_MakeString("base64"), PP_TRUE), 208 PropertyTestData( 209 ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING), 210 PP_MakeString(""), PP_TRUE), 211 PropertyTestData( 212 ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING), 213 PP_MakeUndefined(), PP_TRUE), 214 PropertyTestData( 215 ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT), 216 PP_MakeString("My Crazy Plugin"), PP_TRUE), 217 PropertyTestData( 218 ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT), 219 PP_MakeString(""), PP_TRUE), 220 PropertyTestData( 221 ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT), 222 PP_MakeUndefined(), PP_TRUE), 223 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL), 224 PP_MakeUndefined(), PP_FALSE), 225 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD), 226 PP_MakeUndefined(), PP_FALSE), 227 PropertyTestData( 228 ID_STR(PP_URLREQUESTPROPERTY_HEADERS), 229 PP_MakeString("Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA=="), 230 PP_TRUE), 231 PropertyTestData( 232 ID_STR(PP_URLREQUESTPROPERTY_HEADERS), 233 PP_MakeString("Accept-Encoding: *\n" 234 "Accept-Charset: iso-8859-5, unicode-1-1;q=0.8"), 235 PP_TRUE), 236 PropertyTestData( 237 ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD), 238 PP_MakeInt32(0), PP_TRUE), 239 PropertyTestData( 240 ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD), 241 PP_MakeInt32(100), PP_TRUE), 242 PropertyTestData( 243 ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD), 244 PP_MakeInt32(0), PP_TRUE), 245 PropertyTestData( 246 ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD), 247 PP_MakeInt32(100), PP_TRUE), 248 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL), 249 PP_MakeString("::::::::::::"), PP_TRUE), 250 PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD), 251 PP_MakeString("INVALID"), PP_TRUE), 252 PropertyTestData( 253 ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING), 254 PP_MakeString("invalid"), PP_TRUE), 255 PropertyTestData( 256 ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD), 257 PP_MakeInt32(-100), PP_TRUE), 258 PropertyTestData( 259 ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD), 260 PP_MakeInt32(-100), PP_TRUE), 261 262 }; 263 std::string error; 264 265 PP_Resource url_request = ppb_url_request_interface_->Create( 266 instance_->pp_instance()); 267 if (url_request == kInvalidResource) 268 error = "Failed to create a URLRequestInfo"; 269 270 // Loop over all test data even if we encountered an error to release vars. 271 for (size_t i = 0; 272 i < sizeof(test_data) / sizeof(test_data[0]); 273 ++i) { 274 if (error.empty() && test_data[i].expected_value != 275 ppb_url_request_interface_->SetProperty(url_request, 276 test_data[i].property, 277 test_data[i].var)) { 278 pp::Var var(pp::Var::DontManage(), test_data[i].var); 279 error = std::string("Setting property ") + 280 test_data[i].property_name + " to " + var.DebugString() + 281 " did not return " + (test_data[i].expected_value ? "True" : "False"); 282 error = test_data[i].property_name; 283 } 284 ppb_var_interface_->Release(test_data[i].var); 285 } 286 287 ppb_core_interface_->ReleaseResource(url_request); 288 return error; // == PASS() if empty. 289 } 290 291 std::string TestURLRequest::LoadAndCompareBody( 292 PP_Resource url_request, const std::string& expected_body) { 293 TestCompletionCallback callback(instance_->pp_instance(), PP_REQUIRED); 294 295 PP_Resource url_loader = 296 ppb_url_loader_interface_->Create(instance_->pp_instance()); 297 ASSERT_NE(kInvalidResource, url_loader); 298 299 callback.WaitForResult(ppb_url_loader_interface_->Open( 300 url_loader, url_request, 301 callback.GetCallback().pp_completion_callback())); 302 CHECK_CALLBACK_BEHAVIOR(callback); 303 ASSERT_EQ(PP_OK, callback.result()); 304 305 std::string error; 306 PP_Resource url_response = 307 ppb_url_loader_interface_->GetResponseInfo(url_loader); 308 if (url_response == kInvalidResource) { 309 error = "PPB_URLLoader::GetResponseInfo() returned invalid resource"; 310 } else { 311 PP_Var status = ppb_url_response_interface_->GetProperty( 312 url_response, PP_URLRESPONSEPROPERTY_STATUSCODE); 313 if (status.type != PP_VARTYPE_INT32 && status.value.as_int != 200) 314 error = ReportError("PPB_URLLoader::Open() status", status.value.as_int); 315 316 std::string actual_body; 317 for (; error.empty();) { // Read the entire body in this loop. 318 const size_t kBufferSize = 32; 319 char buf[kBufferSize]; 320 callback.WaitForResult(ppb_url_loader_interface_->ReadResponseBody( 321 url_loader, buf, kBufferSize, 322 callback.GetCallback().pp_completion_callback())); 323 if (callback.failed()) 324 error.assign(callback.errors()); 325 else if (callback.result() < PP_OK) 326 error.assign(ReportError("PPB_URLLoader::ReadResponseBody()", 327 callback.result())); 328 if (callback.result() <= PP_OK || callback.failed()) 329 break; 330 actual_body.append(buf, callback.result()); 331 } 332 if (actual_body != expected_body) 333 error = "PPB_URLLoader::ReadResponseBody() read unexpected response."; 334 } 335 ppb_core_interface_->ReleaseResource(url_response); 336 337 ppb_url_loader_interface_->Close(url_loader); 338 ppb_core_interface_->ReleaseResource(url_loader); 339 return error; 340 } 341 342 // Tests 343 // PP_Bool AppendDataToBody( 344 // PP_Resource request, const void* data, uint32_t len); 345 std::string TestURLRequest::TestAppendDataToBody() { 346 PP_Resource url_request = ppb_url_request_interface_->Create( 347 instance_->pp_instance()); 348 ASSERT_NE(url_request, kInvalidResource); 349 350 std::string postdata("sample postdata"); 351 PP_Var post_string_var = PP_MakeString("POST"); 352 PP_Var echo_string_var = PP_MakeString("/echo"); 353 354 // NULL pointer causes a crash. In general PPAPI implementation does not 355 // test for NULL because they are just a special case of bad pointers that 356 // are not detectable if set to point to an object that does not exist. 357 358 // Invalid resource should fail. 359 ASSERT_EQ(PP_FALSE, ppb_url_request_interface_->AppendDataToBody( 360 kInvalidResource, postdata.data(), postdata.length())); 361 362 // Append data and POST to echoing web server. 363 ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty( 364 url_request, PP_URLREQUESTPROPERTY_METHOD, post_string_var)); 365 ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty( 366 url_request, PP_URLREQUESTPROPERTY_URL, echo_string_var)); 367 368 // Append data to body and verify the body is what we expect. 369 ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->AppendDataToBody( 370 url_request, postdata.data(), postdata.length())); 371 std::string error = LoadAndCompareBody(url_request, postdata); 372 373 ppb_var_interface_->Release(post_string_var); 374 ppb_var_interface_->Release(echo_string_var); 375 ppb_core_interface_->ReleaseResource(url_request); 376 return error; // == PASS() if empty. 377 } 378 379 std::string TestURLRequest::TestAppendFileToBody() { 380 PP_Resource url_request = ppb_url_request_interface_->Create( 381 instance_->pp_instance()); 382 ASSERT_NE(url_request, kInvalidResource); 383 384 TestCompletionCallback callback(instance_->pp_instance(), callback_type()); 385 386 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); 387 callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); 388 CHECK_CALLBACK_BEHAVIOR(callback); 389 ASSERT_EQ(PP_OK, callback.result()); 390 391 pp::FileRef ref(file_system, "/test_file"); 392 pp::FileIO io(instance_); 393 callback.WaitForResult(io.Open(ref, 394 PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE, 395 callback.GetCallback())); 396 CHECK_CALLBACK_BEHAVIOR(callback); 397 ASSERT_EQ(PP_OK, callback.result()); 398 399 std::string append_data = "hello\n"; 400 callback.WaitForResult(io.Write(0, 401 append_data.c_str(), 402 append_data.size(), 403 callback.GetCallback())); 404 CHECK_CALLBACK_BEHAVIOR(callback); 405 ASSERT_EQ(static_cast<int32_t>(append_data.size()), callback.result()); 406 407 PP_Var post_string_var = PP_MakeString("POST"); 408 PP_Var echo_string_var = PP_MakeString("/echo"); 409 410 // NULL pointer causes a crash. In general PPAPI implementation does not 411 // test for NULL because they are just a special case of bad pointers that 412 // are not detectable if set to point to an object that does not exist. 413 414 // Invalid resource should fail. 415 ASSERT_EQ(PP_FALSE, ppb_url_request_interface_->AppendFileToBody( 416 kInvalidResource, ref.pp_resource(), 0, -1, 0)); 417 418 // Append data and POST to echoing web server. 419 ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty( 420 url_request, PP_URLREQUESTPROPERTY_METHOD, post_string_var)); 421 ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty( 422 url_request, PP_URLREQUESTPROPERTY_URL, echo_string_var)); 423 424 // Append file to body and verify the body is what we expect. 425 ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->AppendFileToBody( 426 url_request, ref.pp_resource(), 0, -1, 0)); 427 std::string error = LoadAndCompareBody(url_request, append_data); 428 429 ppb_var_interface_->Release(post_string_var); 430 ppb_var_interface_->Release(echo_string_var); 431 ppb_core_interface_->ReleaseResource(url_request); 432 return error; // == PASS() if empty. 433 } 434 435 // Allocates and manipulates a large number of resources. 436 std::string TestURLRequest::TestStress() { 437 const int kManyResources = 500; 438 PP_Resource url_request_info[kManyResources]; 439 440 std::string error; 441 int num_created = kManyResources; 442 for (int i = 0; i < kManyResources; i++) { 443 url_request_info[i] = ppb_url_request_interface_->Create( 444 instance_->pp_instance()); 445 if (url_request_info[i] == kInvalidResource) { 446 error = "Create() failed"; 447 } else if (PP_FALSE == ppb_url_request_interface_->IsURLRequestInfo( 448 url_request_info[i])) { 449 error = "IsURLRequestInfo() failed"; 450 } else if (PP_FALSE == ppb_url_request_interface_->SetProperty( 451 url_request_info[i], 452 PP_URLREQUESTPROPERTY_STREAMTOFILE, 453 PP_MakeBool(PP_FALSE))) { 454 error = "SetProperty() failed"; 455 } 456 if (!error.empty()) { 457 num_created = i + 1; 458 break; 459 } 460 } 461 for (int i = 0; i < num_created; i++) { 462 ppb_core_interface_->ReleaseResource(url_request_info[i]); 463 if (PP_TRUE == 464 ppb_url_request_interface_->IsURLRequestInfo(url_request_info[i])) 465 error = "IsURLREquestInfo() succeeded after release"; 466 } 467 return error; // == PASS() if empty. 468 } 469