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