1 // Copyright 2014 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 "fake_ppapi/fake_pepper_interface_url_loader.h" 6 7 #include <string.h> 8 #include <strings.h> 9 10 #include <algorithm> 11 #include <sstream> 12 13 #include "gtest/gtest.h" 14 15 #include "nacl_io/osinttypes.h" 16 17 namespace { 18 19 bool GetHeaderValue(const std::string& headers, 20 const std::string& key, 21 std::string* out_value) { 22 out_value->clear(); 23 24 size_t offset = 0; 25 while (offset != std::string::npos) { 26 // Find the next colon; this separates the key from the value. 27 size_t colon = headers.find(':', offset); 28 if (colon == std::string::npos) 29 return false; 30 31 // Find the newline; this separates the value from the next header. 32 size_t newline = headers.find('\n', offset); 33 if (strncasecmp(key.c_str(), &headers.data()[offset], key.size()) != 0) { 34 // Key doesn't match, skip to next header. 35 offset = newline; 36 continue; 37 } 38 39 // Key matches, extract value. First, skip leading spaces. 40 size_t nonspace = headers.find_first_not_of(' ', colon + 1); 41 if (nonspace == std::string::npos) 42 return false; 43 44 out_value->assign(headers, nonspace, newline - nonspace); 45 return true; 46 } 47 48 return false; 49 } 50 51 class FakeInstanceResource : public FakeResource { 52 public: 53 FakeInstanceResource() : server_template(NULL) {} 54 static const char* classname() { return "FakeInstanceResource"; } 55 56 FakeURLLoaderServer* server_template; // Weak reference. 57 }; 58 59 class FakeURLLoaderResource : public FakeResource { 60 public: 61 FakeURLLoaderResource() 62 : manager(NULL), 63 server(NULL), 64 entity(NULL), 65 response(0), 66 read_offset(0) {} 67 68 virtual void Destroy() { 69 EXPECT_TRUE(manager != NULL); 70 if (response != 0) 71 manager->Release(response); 72 delete server; 73 } 74 75 static const char* classname() { return "FakeURLLoaderResource"; } 76 77 FakeResourceManager* manager; // Weak reference. 78 FakeURLLoaderServer* server; 79 FakeURLLoaderEntity* entity; // Weak reference. 80 PP_Resource response; 81 off_t read_offset; 82 off_t read_end; 83 }; 84 85 class FakeURLRequestInfoResource : public FakeResource { 86 public: 87 FakeURLRequestInfoResource() {} 88 static const char* classname() { return "FakeURLRequestInfoResource"; } 89 90 std::string url; 91 std::string method; 92 std::string headers; 93 }; 94 95 class FakeURLResponseInfoResource : public FakeResource { 96 public: 97 FakeURLResponseInfoResource() : status_code(0) {} 98 static const char* classname() { return "FakeURLResponseInfoResource"; } 99 100 int status_code; 101 std::string url; 102 std::string headers; 103 }; 104 105 // Helper function to call the completion callback if it is defined (an 106 // asynchronous call), or return the result directly if it isn't (a synchronous 107 // call). 108 // 109 // Use like this: 110 // if (<some error condition>) 111 // return RunCompletionCallback(callback, PP_ERROR_FUBAR); 112 // 113 // /* Everything worked OK */ 114 // return RunCompletionCallback(callback, PP_OK); 115 int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) { 116 if (callback->func) { 117 PP_RunCompletionCallback(callback, result); 118 return PP_OK_COMPLETIONPENDING; 119 } 120 return result; 121 } 122 123 void HandleContentLength(FakeURLLoaderResource* loader, 124 FakeURLResponseInfoResource* response, 125 FakeURLLoaderEntity* entity) { 126 off_t content_length = entity->size(); 127 if (!loader->server->send_content_length()) 128 return; 129 130 std::ostringstream ss; 131 ss << "Content-Length: " << content_length << "\n"; 132 response->headers += ss.str(); 133 } 134 135 void HandlePartial(FakeURLLoaderResource* loader, 136 FakeURLRequestInfoResource* request, 137 FakeURLResponseInfoResource* response, 138 FakeURLLoaderEntity* entity) { 139 if (!loader->server->allow_partial()) 140 return; 141 142 // Read the RFC on byte ranges for more info: 143 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1 144 std::string range; 145 if (!GetHeaderValue(request->headers, "Range", &range)) 146 return; 147 148 // We don't support all range requests, just bytes=<num>-<num> 149 off_t lo; 150 off_t hi; 151 if (sscanf(range.c_str(), "bytes=%" SCNi64 "-%" SCNi64, &lo, &hi) != 2) { 152 // Couldn't parse the range value. 153 return; 154 } 155 156 off_t content_length = entity->size(); 157 if (lo > content_length) { 158 // Trying to start reading past the end of the entity is 159 // unsatisfiable. 160 response->status_code = 416; // Request range not satisfiable. 161 return; 162 } 163 164 // Clamp the hi value to the content length. 165 if (hi >= content_length) 166 hi = content_length - 1; 167 168 if (lo > hi) { 169 // Bad range, ignore it and return the full result. 170 return; 171 } 172 173 // The range is a closed interval; e.g. 0-10 is 11 bytes. We'll 174 // store it as a half-open interval instead--it's more natural 175 // in C that way. 176 loader->read_offset = lo; 177 loader->read_end = hi + 1; 178 179 // Also add a "Content-Range" response header. 180 std::ostringstream ss; 181 ss << "Content-Range: bytes " << lo << "-" << hi << "/" << content_length 182 << "\n"; 183 response->headers += ss.str(); 184 185 response->status_code = 206; // Partial content 186 } 187 188 } // namespace 189 190 FakeURLLoaderEntity::FakeURLLoaderEntity(const std::string& body) 191 : body_(body), size_(body_.size()), repeat_(false) { 192 } 193 194 // Rather than specifying the entire file, specify a string to repeat, and the 195 // full length. This lets us test extremely large files without having to store 196 // them in memory. 197 FakeURLLoaderEntity::FakeURLLoaderEntity(const std::string& to_repeat, 198 off_t size) 199 : body_(to_repeat), size_(size), repeat_(true) { 200 } 201 202 size_t FakeURLLoaderEntity::Read(void* buffer, size_t count, off_t offset) { 203 off_t max_read_count = 204 std::max<off_t>(std::min<off_t>(size_ - offset, 0xffffffff), 0); 205 size_t bytes_to_read = std::min(count, static_cast<size_t>(max_read_count)); 206 207 if (repeat_) { 208 size_t src_size = body_.size(); 209 char* dst = static_cast<char*>(buffer); 210 const char* src = body_.data(); 211 size_t bytes_left = bytes_to_read; 212 213 size_t src_offset = static_cast<size_t>(offset % src_size); 214 if (src_offset != 0) { 215 // Copy enough to align. 216 size_t bytes_to_copy = std::min(bytes_left, src_size - src_offset); 217 memcpy(dst, src + src_offset, bytes_to_copy); 218 dst += bytes_to_copy; 219 bytes_left -= bytes_to_copy; 220 } 221 222 // Copy the body N times. 223 for (size_t i = bytes_left / src_size; i > 0; --i) { 224 memcpy(dst, src, src_size); 225 dst += src_size; 226 bytes_left -= src_size; 227 } 228 229 // Copy the rest of the bytes, < src_size. 230 if (bytes_left > 0) { 231 assert(bytes_left < src_size); 232 memcpy(dst, src, bytes_left); 233 } 234 } else { 235 memcpy(buffer, &body_.data()[offset], bytes_to_read); 236 } 237 238 return bytes_to_read; 239 } 240 241 FakeURLLoaderServer::FakeURLLoaderServer() 242 : max_read_size_(0), 243 send_content_length_(false), 244 allow_partial_(false), 245 allow_head_(true) { 246 } 247 248 void FakeURLLoaderServer::Clear() { 249 entity_map_.clear(); 250 } 251 252 bool FakeURLLoaderServer::AddEntity(const std::string& url, 253 const std::string& body, 254 FakeURLLoaderEntity** out_entity) { 255 EntityMap::iterator iter = entity_map_.find(url); 256 if (iter != entity_map_.end()) { 257 if (out_entity) 258 *out_entity = NULL; 259 return false; 260 } 261 262 FakeURLLoaderEntity entity(body); 263 std::pair<EntityMap::iterator, bool> result = 264 entity_map_.insert(EntityMap::value_type(url, entity)); 265 266 EXPECT_EQ(true, result.second); 267 if (out_entity) 268 *out_entity = &result.first->second; 269 return true; 270 } 271 272 bool FakeURLLoaderServer::AddEntity(const std::string& url, 273 const std::string& body, 274 off_t size, 275 FakeURLLoaderEntity** out_entity) { 276 EntityMap::iterator iter = entity_map_.find(url); 277 if (iter != entity_map_.end()) { 278 if (out_entity) 279 *out_entity = NULL; 280 return false; 281 } 282 283 FakeURLLoaderEntity entity(body, size); 284 std::pair<EntityMap::iterator, bool> result = 285 entity_map_.insert(EntityMap::value_type(url, entity)); 286 287 EXPECT_EQ(true, result.second); 288 if (out_entity) 289 *out_entity = &result.first->second; 290 return true; 291 } 292 293 bool FakeURLLoaderServer::SetBlobEntity(const std::string& url, 294 const std::string& body, 295 FakeURLLoaderEntity** out_entity) { 296 set_allow_partial(true); 297 set_allow_head(false); 298 return AddEntity(url, body, out_entity); 299 } 300 301 bool FakeURLLoaderServer::AddError(const std::string& url, 302 int http_status_code) { 303 ErrorMap::iterator iter = error_map_.find(url); 304 if (iter != error_map_.end()) 305 return false; 306 307 error_map_[url] = http_status_code; 308 return true; 309 } 310 311 FakeURLLoaderEntity* FakeURLLoaderServer::GetEntity(const std::string& url) { 312 EntityMap::iterator iter = entity_map_.find(url); 313 if (iter == entity_map_.end()) 314 return NULL; 315 return &iter->second; 316 } 317 318 int FakeURLLoaderServer::GetError(const std::string& url) { 319 ErrorMap::iterator iter = error_map_.find(url); 320 if (iter == error_map_.end()) 321 return 0; 322 return iter->second; 323 } 324 325 FakeURLLoaderInterface::FakeURLLoaderInterface( 326 FakeCoreInterface* core_interface) 327 : core_interface_(core_interface) { 328 } 329 330 PP_Resource FakeURLLoaderInterface::Create(PP_Instance instance) { 331 FakeInstanceResource* instance_resource = 332 core_interface_->resource_manager()->Get<FakeInstanceResource>(instance); 333 if (instance_resource == NULL) 334 return PP_ERROR_BADRESOURCE; 335 336 FakeURLLoaderResource* loader_resource = new FakeURLLoaderResource; 337 loader_resource->manager = core_interface_->resource_manager(); 338 loader_resource->server = 339 new FakeURLLoaderServer(*instance_resource->server_template); 340 341 return CREATE_RESOURCE(core_interface_->resource_manager(), 342 FakeURLLoaderResource, 343 loader_resource); 344 } 345 346 int32_t FakeURLLoaderInterface::Open(PP_Resource loader, 347 PP_Resource request, 348 PP_CompletionCallback callback) { 349 FakeURLLoaderResource* loader_resource = 350 core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader); 351 if (loader_resource == NULL) 352 return PP_ERROR_BADRESOURCE; 353 354 FakeURLRequestInfoResource* request_resource = 355 core_interface_->resource_manager()->Get<FakeURLRequestInfoResource>( 356 request); 357 if (request_resource == NULL) 358 return PP_ERROR_BADRESOURCE; 359 360 // Create a response resource. 361 FakeURLResponseInfoResource* response_resource = 362 new FakeURLResponseInfoResource; 363 loader_resource->response = 364 CREATE_RESOURCE(core_interface_->resource_manager(), 365 FakeURLResponseInfoResource, 366 response_resource); 367 368 loader_resource->entity = NULL; 369 loader_resource->read_offset = 0; 370 loader_resource->read_end = 0; 371 372 // Get the URL from the request info. 373 std::string url = request_resource->url; 374 std::string method = request_resource->method; 375 376 response_resource->url = url; 377 // TODO(binji): allow this to be set? 378 response_resource->headers.clear(); 379 380 // Check the error map first, to see if this URL should produce an error. 381 EXPECT_TRUE(NULL != loader_resource->server); 382 int http_status_code = loader_resource->server->GetError(url); 383 if (http_status_code != 0) { 384 // Got an error, return that in the response. 385 response_resource->status_code = http_status_code; 386 return RunCompletionCallback(&callback, PP_OK); 387 } 388 389 // Look up the URL in the loader resource entity map. 390 FakeURLLoaderEntity* entity = loader_resource->server->GetEntity(url); 391 response_resource->status_code = entity ? 200 : 404; 392 393 if (method == "GET") { 394 loader_resource->entity = entity; 395 } else if (method != "HEAD" || !loader_resource->server->allow_head()) { 396 response_resource->status_code = 405; // Method not allowed. 397 return RunCompletionCallback(&callback, PP_OK); 398 } 399 400 if (entity != NULL) { 401 off_t content_length = entity->size(); 402 loader_resource->read_end = content_length; 403 HandleContentLength(loader_resource, response_resource, entity); 404 HandlePartial(loader_resource, request_resource, response_resource, entity); 405 } 406 407 // Call the callback. 408 return RunCompletionCallback(&callback, PP_OK); 409 } 410 411 PP_Resource FakeURLLoaderInterface::GetResponseInfo(PP_Resource loader) { 412 FakeURLLoaderResource* loader_resource = 413 core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader); 414 if (loader_resource == NULL) 415 return 0; 416 417 // Returned resources have an implicit AddRef. 418 core_interface_->resource_manager()->AddRef(loader_resource->response); 419 return loader_resource->response; 420 } 421 422 int32_t FakeURLLoaderInterface::ReadResponseBody( 423 PP_Resource loader, 424 void* buffer, 425 int32_t bytes_to_read, 426 PP_CompletionCallback callback) { 427 FakeURLLoaderResource* loader_resource = 428 core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader); 429 if (loader_resource == NULL) 430 return PP_ERROR_BADRESOURCE; 431 432 if (loader_resource->entity == NULL) 433 // TODO(binji): figure out the correct error here. 434 return PP_ERROR_FAILED; 435 436 // Allow the test to specify how much the "server" should send in each call 437 // to ReadResponseBody. A max_read_size of 0 means read as much as the 438 // buffer will allow. 439 size_t server_max_read_size = loader_resource->server->max_read_size(); 440 if (server_max_read_size != 0) 441 bytes_to_read = std::min<int32_t>(bytes_to_read, server_max_read_size); 442 443 size_t bytes_read = loader_resource->entity->Read( 444 buffer, bytes_to_read, loader_resource->read_offset); 445 loader_resource->read_offset += bytes_read; 446 447 return RunCompletionCallback(&callback, bytes_read); 448 } 449 450 void FakeURLLoaderInterface::Close(PP_Resource loader) { 451 FakeURLLoaderResource* loader_resource = 452 core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader); 453 if (loader_resource == NULL) 454 return; 455 456 core_interface_->resource_manager()->Release(loader_resource->response); 457 458 loader_resource->server = NULL; 459 loader_resource->entity = NULL; 460 loader_resource->response = 0; 461 loader_resource->read_offset = 0; 462 } 463 464 FakeURLRequestInfoInterface::FakeURLRequestInfoInterface( 465 FakeCoreInterface* core_interface, 466 FakeVarInterface* var_interface) 467 : core_interface_(core_interface), var_interface_(var_interface) { 468 } 469 470 PP_Resource FakeURLRequestInfoInterface::Create(PP_Instance instance) { 471 FakeInstanceResource* instance_resource = 472 core_interface_->resource_manager()->Get<FakeInstanceResource>(instance); 473 if (instance_resource == NULL) 474 return PP_ERROR_BADRESOURCE; 475 476 return CREATE_RESOURCE(core_interface_->resource_manager(), 477 FakeURLRequestInfoResource, 478 new FakeURLRequestInfoResource); 479 } 480 481 PP_Bool FakeURLRequestInfoInterface::SetProperty(PP_Resource request, 482 PP_URLRequestProperty property, 483 PP_Var value) { 484 FakeURLRequestInfoResource* request_resource = 485 core_interface_->resource_manager()->Get<FakeURLRequestInfoResource>( 486 request); 487 if (request_resource == NULL) 488 return PP_FALSE; 489 490 switch (property) { 491 case PP_URLREQUESTPROPERTY_URL: { 492 if (value.type != PP_VARTYPE_STRING) 493 return PP_FALSE; 494 495 uint32_t len; 496 const char* url = var_interface_->VarToUtf8(value, &len); 497 if (url == NULL) 498 return PP_FALSE; 499 500 request_resource->url = url; 501 var_interface_->Release(value); 502 return PP_TRUE; 503 } 504 case PP_URLREQUESTPROPERTY_METHOD: { 505 if (value.type != PP_VARTYPE_STRING) 506 return PP_FALSE; 507 508 uint32_t len; 509 const char* url = var_interface_->VarToUtf8(value, &len); 510 if (url == NULL) 511 return PP_FALSE; 512 513 request_resource->method = url; 514 var_interface_->Release(value); 515 return PP_TRUE; 516 } 517 case PP_URLREQUESTPROPERTY_HEADERS: { 518 if (value.type != PP_VARTYPE_STRING) 519 return PP_FALSE; 520 521 uint32_t len; 522 const char* url = var_interface_->VarToUtf8(value, &len); 523 if (url == NULL) 524 return PP_FALSE; 525 526 request_resource->headers = url; 527 var_interface_->Release(value); 528 return PP_TRUE; 529 } 530 case PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS: { 531 if (value.type != PP_VARTYPE_BOOL) 532 return PP_FALSE; 533 // Throw the value away for now. TODO(binji): add tests for this. 534 return PP_TRUE; 535 } 536 case PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS: { 537 if (value.type != PP_VARTYPE_BOOL) 538 return PP_FALSE; 539 // Throw the value away for now. TODO(binji): add tests for this. 540 return PP_TRUE; 541 } 542 default: 543 EXPECT_TRUE(false) << "Unimplemented property " << property 544 << " in " 545 "FakeURLRequestInfoInterface::SetProperty"; 546 return PP_FALSE; 547 } 548 } 549 550 FakeURLResponseInfoInterface::FakeURLResponseInfoInterface( 551 FakeCoreInterface* core_interface, 552 FakeVarInterface* var_interface) 553 : core_interface_(core_interface), var_interface_(var_interface) { 554 } 555 556 PP_Var FakeURLResponseInfoInterface::GetProperty( 557 PP_Resource response, 558 PP_URLResponseProperty property) { 559 FakeURLResponseInfoResource* response_resource = 560 core_interface_->resource_manager()->Get<FakeURLResponseInfoResource>( 561 response); 562 if (response_resource == NULL) 563 return PP_Var(); 564 565 switch (property) { 566 case PP_URLRESPONSEPROPERTY_URL: 567 return var_interface_->VarFromUtf8(response_resource->url.data(), 568 response_resource->url.size()); 569 570 case PP_URLRESPONSEPROPERTY_STATUSCODE: 571 return PP_MakeInt32(response_resource->status_code); 572 573 case PP_URLRESPONSEPROPERTY_HEADERS: 574 return var_interface_->VarFromUtf8(response_resource->headers.data(), 575 response_resource->headers.size()); 576 default: 577 EXPECT_TRUE(false) << "Unimplemented property " << property 578 << " in " 579 "FakeURLResponseInfoInterface::GetProperty"; 580 return PP_Var(); 581 } 582 } 583 584 FakePepperInterfaceURLLoader::FakePepperInterfaceURLLoader() 585 : core_interface_(&resource_manager_), 586 var_interface_(&var_manager_), 587 url_loader_interface_(&core_interface_), 588 url_request_info_interface_(&core_interface_, &var_interface_), 589 url_response_info_interface_(&core_interface_, &var_interface_) { 590 FakeInstanceResource* instance_resource = new FakeInstanceResource; 591 instance_resource->server_template = &server_template_; 592 instance_ = CREATE_RESOURCE(core_interface_.resource_manager(), 593 FakeInstanceResource, 594 instance_resource); 595 } 596 597 FakePepperInterfaceURLLoader::~FakePepperInterfaceURLLoader() { 598 core_interface_.ReleaseResource(instance_); 599 } 600 601 nacl_io::CoreInterface* FakePepperInterfaceURLLoader::GetCoreInterface() { 602 return &core_interface_; 603 } 604 605 nacl_io::VarInterface* FakePepperInterfaceURLLoader::GetVarInterface() { 606 return &var_interface_; 607 } 608 609 nacl_io::URLLoaderInterface* 610 FakePepperInterfaceURLLoader::GetURLLoaderInterface() { 611 return &url_loader_interface_; 612 } 613 614 nacl_io::URLRequestInfoInterface* 615 FakePepperInterfaceURLLoader::GetURLRequestInfoInterface() { 616 return &url_request_info_interface_; 617 } 618 619 nacl_io::URLResponseInfoInterface* 620 FakePepperInterfaceURLLoader::GetURLResponseInfoInterface() { 621 return &url_response_info_interface_; 622 } 623