Home | History | Annotate | Download | only in fake_ppapi
      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