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