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