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