| Index: native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.cc
|
| diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.cc b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1b6b92cd5cb0a2ff0ae2624e497e807fb07d22c1
|
| --- /dev/null
|
| +++ b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.cc
|
| @@ -0,0 +1,502 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "fake_pepper_interface_url_loader.h"
|
| +
|
| +#include <string.h>
|
| +#include <strings.h>
|
| +
|
| +#include <algorithm>
|
| +#include <sstream>
|
| +
|
| +#include "gtest/gtest.h"
|
| +
|
| +namespace {
|
| +
|
| +bool GetHeaderValue(const std::string& headers, const std::string& key,
|
| + std::string* out_value) {
|
| + out_value->clear();
|
| +
|
| + size_t offset = 0;
|
| + while (offset != std::string::npos) {
|
| + // Find the next colon; this separates the key from the value.
|
| + size_t colon = headers.find(':', offset);
|
| + if (colon == std::string::npos)
|
| + return false;
|
| +
|
| + // Find the newline; this separates the value from the next header.
|
| + size_t newline = headers.find('\n', offset);
|
| + if (strncasecmp(key.c_str(), &headers.data()[offset], key.size()) != 0) {
|
| + // Key doesn't match, skip to next header.
|
| + offset = newline;
|
| + continue;
|
| + }
|
| +
|
| + // Key matches, extract value. First, skip leading spaces.
|
| + size_t nonspace = headers.find_first_not_of(' ', colon + 1);
|
| + if (nonspace == std::string::npos)
|
| + return false;
|
| +
|
| + out_value->assign(headers, nonspace, newline - nonspace);
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +class FakeInstanceResource : public FakeResource {
|
| + public:
|
| + FakeInstanceResource() : server_template(NULL) {}
|
| + static const char* classname() { return "FakeInstanceResource"; }
|
| +
|
| + FakeURLLoaderServer* server_template; // Weak reference.
|
| +};
|
| +
|
| +class FakeURLLoaderResource : public FakeResource {
|
| + public:
|
| + FakeURLLoaderResource()
|
| + : manager(NULL),
|
| + server(NULL),
|
| + entity(NULL),
|
| + response(0),
|
| + read_offset(0) {}
|
| +
|
| + virtual void Destroy() {
|
| + EXPECT_TRUE(manager != NULL);
|
| + if (response != 0)
|
| + manager->Release(response);
|
| + }
|
| +
|
| + static const char* classname() { return "FakeURLLoaderResource"; }
|
| +
|
| + FakeResourceManager* manager; // Weak reference.
|
| + FakeURLLoaderServer* server; // Weak reference.
|
| + FakeURLLoaderEntity* entity; // Weak reference.
|
| + PP_Resource response;
|
| + size_t read_offset;
|
| + size_t read_end;
|
| +};
|
| +
|
| +class FakeURLRequestInfoResource : public FakeResource {
|
| + public:
|
| + FakeURLRequestInfoResource() {}
|
| + static const char* classname() { return "FakeURLRequestInfoResource"; }
|
| +
|
| + std::string url;
|
| + std::string method;
|
| + std::string headers;
|
| +};
|
| +
|
| +class FakeURLResponseInfoResource : public FakeResource {
|
| + public:
|
| + FakeURLResponseInfoResource() : status_code(0) {}
|
| + static const char* classname() { return "FakeURLResponseInfoResource"; }
|
| +
|
| + int status_code;
|
| + std::string url;
|
| + std::string headers;
|
| +};
|
| +
|
| +// Helper function to call the completion callback if it is defined (an
|
| +// asynchronous call), or return the result directly if it isn't (a synchronous
|
| +// call).
|
| +//
|
| +// Use like this:
|
| +// if (<some error condition>)
|
| +// return RunCompletionCallback(callback, PP_ERROR_FUBAR);
|
| +//
|
| +// /* Everything worked OK */
|
| +// return RunCompletionCallback(callback, PP_OK);
|
| +int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) {
|
| + if (callback->func) {
|
| + PP_RunCompletionCallback(callback, result);
|
| + return PP_OK_COMPLETIONPENDING;
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +FakeURLLoaderEntity::FakeURLLoaderEntity(const std::string& body)
|
| + : body_(body) {}
|
| +
|
| +FakeURLLoaderServer::FakeURLLoaderServer()
|
| + : max_read_size_(0), send_content_length_(false), allow_partial_(false) {}
|
| +
|
| +void FakeURLLoaderServer::Clear() {
|
| + entity_map_.clear();
|
| +}
|
| +
|
| +bool FakeURLLoaderServer::AddEntity(const std::string& url,
|
| + const std::string& body,
|
| + FakeURLLoaderEntity** out_entity) {
|
| + EntityMap::iterator iter = entity_map_.find(url);
|
| + if (iter != entity_map_.end()) {
|
| + if (out_entity)
|
| + *out_entity = NULL;
|
| + return false;
|
| + }
|
| +
|
| + FakeURLLoaderEntity entity(body);
|
| + std::pair<EntityMap::iterator, bool> result =
|
| + entity_map_.insert(EntityMap::value_type(url, entity));
|
| +
|
| + EXPECT_EQ(true, result.second);
|
| + if (out_entity)
|
| + *out_entity = &result.first->second;
|
| + return true;
|
| +}
|
| +
|
| +bool FakeURLLoaderServer::AddError(const std::string& url,
|
| + int http_status_code) {
|
| + ErrorMap::iterator iter = error_map_.find(url);
|
| + if (iter != error_map_.end())
|
| + return false;
|
| +
|
| + error_map_[url] = http_status_code;
|
| + return true;
|
| +}
|
| +
|
| +FakeURLLoaderEntity* FakeURLLoaderServer::GetEntity(const std::string& url) {
|
| + EntityMap::iterator iter = entity_map_.find(url);
|
| + if (iter == entity_map_.end())
|
| + return NULL;
|
| + return &iter->second;
|
| +}
|
| +
|
| +int FakeURLLoaderServer::GetError(const std::string& url) {
|
| + ErrorMap::iterator iter = error_map_.find(url);
|
| + if (iter != error_map_.end())
|
| + return 0;
|
| + return iter->second;
|
| +}
|
| +
|
| +FakeURLLoaderInterface::FakeURLLoaderInterface(
|
| + FakeCoreInterface* core_interface)
|
| + : core_interface_(core_interface) {}
|
| +
|
| +PP_Resource FakeURLLoaderInterface::Create(PP_Instance instance) {
|
| + FakeInstanceResource* instance_resource =
|
| + core_interface_->resource_manager()->Get<FakeInstanceResource>(instance);
|
| + if (instance_resource == NULL)
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + FakeURLLoaderResource* loader_resource = new FakeURLLoaderResource;
|
| + loader_resource->manager = core_interface_->resource_manager();
|
| + loader_resource->server =
|
| + new FakeURLLoaderServer(*instance_resource->server_template);
|
| +
|
| + return CREATE_RESOURCE(core_interface_->resource_manager(),
|
| + FakeURLLoaderResource,
|
| + loader_resource);
|
| +}
|
| +
|
| +int32_t FakeURLLoaderInterface::Open(PP_Resource loader,
|
| + PP_Resource request_info,
|
| + PP_CompletionCallback callback) {
|
| + FakeURLLoaderResource* loader_resource =
|
| + core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader);
|
| + if (loader_resource == NULL)
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + FakeURLRequestInfoResource* request_info_resource =
|
| + core_interface_->resource_manager()->Get<FakeURLRequestInfoResource>(
|
| + request_info);
|
| + if (request_info_resource == NULL)
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + // Create a response resource.
|
| + FakeURLResponseInfoResource* response = new FakeURLResponseInfoResource;
|
| + loader_resource->response =
|
| + CREATE_RESOURCE(core_interface_->resource_manager(),
|
| + FakeURLResponseInfoResource,
|
| + response);
|
| +
|
| + loader_resource->entity = NULL;
|
| + loader_resource->read_offset = 0;
|
| + loader_resource->read_end = 0;
|
| +
|
| + // Get the URL from the request info.
|
| + std::string url = request_info_resource->url;
|
| + std::string method = request_info_resource->method;
|
| +
|
| + response->url = url;
|
| + // TODO(binji): allow this to be set?
|
| + response->headers.clear();
|
| +
|
| + // Check the error map first, to see if this URL should produce an error.
|
| + EXPECT_TRUE(NULL != loader_resource->server);
|
| + int http_status_code = loader_resource->server->GetError(url);
|
| + if (http_status_code != 0) {
|
| + // Got an error, return that in the response.
|
| + response->status_code = http_status_code;
|
| + } else {
|
| + // Look up the URL in the loader resource entity map.
|
| + FakeURLLoaderEntity* entity = loader_resource->server->GetEntity(url);
|
| + response->status_code = entity ? 200 : 404;
|
| +
|
| + if (method == "GET") {
|
| + loader_resource->entity = entity;
|
| + } else if (method == "HEAD") {
|
| + // Do nothing, we only set the status code.
|
| + } else {
|
| + response->status_code = 405; // Method not allowed.
|
| + }
|
| +
|
| + if (entity != NULL) {
|
| + size_t content_length = entity->body().size();
|
| + loader_resource->read_end = content_length;
|
| +
|
| + if (loader_resource->server->send_content_length()) {
|
| + std::ostringstream ss;
|
| + ss << "Content-Length: " << content_length << "\n";
|
| + response->headers += ss.str();
|
| + }
|
| +
|
| + if (loader_resource->server->allow_partial()) {
|
| + std::string headers = request_info_resource->headers;
|
| + std::string range;
|
| + if (GetHeaderValue(headers, "Range", &range)) {
|
| + // We don't support all range requests, just bytes=<num>-<num>
|
| + int lo;
|
| + int hi;
|
| + if (sscanf(range.c_str(), "bytes=%d-%d", &lo, &hi) == 2) {
|
| + // The range is a closed interval; e.g. 0-10 is 11 bytes. We'll
|
| + // store it as a half-open interval instead--it's more natural in
|
| + // C that way.
|
| + loader_resource->read_offset = lo;
|
| + loader_resource->read_end = hi + 1;
|
| +
|
| + // Also add a "Content-Range" response header.
|
| + std::ostringstream ss;
|
| + ss << "Content-Range: "
|
| + << lo << "-" << hi << "/" << content_length << "\n";
|
| + response->headers += ss.str();
|
| +
|
| + response->status_code = 206; // Partial content
|
| + } else {
|
| + // Couldn't parse the range.
|
| + response->status_code = 416; // Request range not satisfiable.
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Call the callback.
|
| + return RunCompletionCallback(&callback, PP_OK);
|
| +}
|
| +
|
| +PP_Resource FakeURLLoaderInterface::GetResponseInfo(PP_Resource loader) {
|
| + FakeURLLoaderResource* loader_resource =
|
| + core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader);
|
| + if (loader_resource == NULL)
|
| + return 0;
|
| +
|
| + // Returned resources have an implicit AddRef.
|
| + core_interface_->resource_manager()->AddRef(loader_resource->response);
|
| + return loader_resource->response;
|
| +}
|
| +
|
| +int32_t FakeURLLoaderInterface::ReadResponseBody(
|
| + PP_Resource loader,
|
| + void* buffer,
|
| + int32_t bytes_to_read,
|
| + PP_CompletionCallback callback) {
|
| + FakeURLLoaderResource* loader_resource =
|
| + core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader);
|
| + if (loader_resource == NULL)
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + if (loader_resource->entity == NULL)
|
| + // TODO(binji): figure out the correct error here.
|
| + return PP_ERROR_FAILED;
|
| +
|
| + const std::string& body = loader_resource->entity->body();
|
| + size_t offset = loader_resource->read_offset;
|
| + // Never read more than is available.
|
| + size_t max_readable = std::max<size_t>(0, body.length() - offset);
|
| + size_t server_max_read_size = loader_resource->server->max_read_size();
|
| + // Allow the test to specify how much the "server" should send in each call
|
| + // to ReadResponseBody. A max_read_size of 0 means read as much as the
|
| + // buffer will allow.
|
| + if (server_max_read_size != 0)
|
| + max_readable = std::min(max_readable, server_max_read_size);
|
| +
|
| + bytes_to_read = std::min(static_cast<size_t>(bytes_to_read), max_readable);
|
| + memcpy(buffer, &body.data()[offset], bytes_to_read);
|
| + loader_resource->read_offset += bytes_to_read;
|
| +
|
| + return RunCompletionCallback(&callback, bytes_to_read);
|
| +}
|
| +
|
| +void FakeURLLoaderInterface::Close(PP_Resource loader) {
|
| + FakeURLLoaderResource* loader_resource =
|
| + core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader);
|
| + if (loader_resource == NULL)
|
| + return;
|
| +
|
| + core_interface_->resource_manager()->Release(loader_resource->response);
|
| +
|
| + loader_resource->server = NULL;
|
| + loader_resource->entity = NULL;
|
| + loader_resource->response = 0;
|
| + loader_resource->read_offset = 0;
|
| +}
|
| +
|
| +FakeURLRequestInfoInterface::FakeURLRequestInfoInterface(
|
| + FakeCoreInterface* core_interface,
|
| + FakeVarInterface* var_interface)
|
| + : core_interface_(core_interface), var_interface_(var_interface) {}
|
| +
|
| +PP_Resource FakeURLRequestInfoInterface::Create(PP_Instance instance) {
|
| + FakeInstanceResource* instance_resource =
|
| + core_interface_->resource_manager()->Get<FakeInstanceResource>(instance);
|
| + if (instance_resource == NULL)
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + return CREATE_RESOURCE(core_interface_->resource_manager(),
|
| + FakeURLRequestInfoResource,
|
| + new FakeURLRequestInfoResource);
|
| +}
|
| +
|
| +PP_Bool FakeURLRequestInfoInterface::SetProperty(PP_Resource request,
|
| + PP_URLRequestProperty property,
|
| + PP_Var value) {
|
| + FakeURLRequestInfoResource* request_resource =
|
| + core_interface_->resource_manager()->Get<FakeURLRequestInfoResource>(
|
| + request);
|
| + if (request_resource == NULL)
|
| + return PP_FALSE;
|
| +
|
| + switch (property) {
|
| + case PP_URLREQUESTPROPERTY_URL: {
|
| + if (value.type != PP_VARTYPE_STRING)
|
| + return PP_FALSE;
|
| +
|
| + uint32_t len;
|
| + const char* url = var_interface_->VarToUtf8(value, &len);
|
| + if (url == NULL)
|
| + return PP_FALSE;
|
| +
|
| + request_resource->url = url;
|
| + var_interface_->Release(value);
|
| + return PP_TRUE;
|
| + }
|
| + case PP_URLREQUESTPROPERTY_METHOD: {
|
| + if (value.type != PP_VARTYPE_STRING)
|
| + return PP_FALSE;
|
| +
|
| + uint32_t len;
|
| + const char* url = var_interface_->VarToUtf8(value, &len);
|
| + if (url == NULL)
|
| + return PP_FALSE;
|
| +
|
| + request_resource->method = url;
|
| + var_interface_->Release(value);
|
| + return PP_TRUE;
|
| + }
|
| + case PP_URLREQUESTPROPERTY_HEADERS: {
|
| + if (value.type != PP_VARTYPE_STRING)
|
| + return PP_FALSE;
|
| +
|
| + uint32_t len;
|
| + const char* url = var_interface_->VarToUtf8(value, &len);
|
| + if (url == NULL)
|
| + return PP_FALSE;
|
| +
|
| + request_resource->headers = url;
|
| + var_interface_->Release(value);
|
| + return PP_TRUE;
|
| + }
|
| + case PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS: {
|
| + if (value.type != PP_VARTYPE_BOOL)
|
| + return PP_FALSE;
|
| + // Throw the value away for now. TODO(binji): add tests for this.
|
| + return PP_TRUE;
|
| + }
|
| + case PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS: {
|
| + if (value.type != PP_VARTYPE_BOOL)
|
| + return PP_FALSE;
|
| + // Throw the value away for now. TODO(binji): add tests for this.
|
| + return PP_TRUE;
|
| + }
|
| + default:
|
| + EXPECT_TRUE(false) << "Unimplemented property " << property
|
| + << " in "
|
| + "FakeURLRequestInfoInterface::SetProperty";
|
| + return PP_FALSE;
|
| + }
|
| +}
|
| +
|
| +FakeURLResponseInfoInterface::FakeURLResponseInfoInterface(
|
| + FakeCoreInterface* core_interface,
|
| + FakeVarInterface* var_interface)
|
| + : core_interface_(core_interface), var_interface_(var_interface) {}
|
| +
|
| +PP_Var FakeURLResponseInfoInterface::GetProperty(
|
| + PP_Resource response,
|
| + PP_URLResponseProperty property) {
|
| + FakeURLResponseInfoResource* response_resource =
|
| + core_interface_->resource_manager()->Get<FakeURLResponseInfoResource>(
|
| + response);
|
| + if (response_resource == NULL)
|
| + return PP_Var();
|
| +
|
| + switch (property) {
|
| + case PP_URLRESPONSEPROPERTY_URL:
|
| + return var_interface_->VarFromUtf8(response_resource->url.data(),
|
| + response_resource->url.size());
|
| +
|
| + case PP_URLRESPONSEPROPERTY_STATUSCODE:
|
| + return PP_MakeInt32(response_resource->status_code);
|
| +
|
| + case PP_URLRESPONSEPROPERTY_HEADERS:
|
| + return var_interface_->VarFromUtf8(response_resource->headers.data(),
|
| + response_resource->headers.size());
|
| + default:
|
| + EXPECT_TRUE(false) << "Unimplemented property " << property
|
| + << " in "
|
| + "FakeURLResponseInfoInterface::GetProperty";
|
| + return PP_Var();
|
| + }
|
| +}
|
| +
|
| +FakePepperInterfaceURLLoader::FakePepperInterfaceURLLoader()
|
| + : url_loader_interface_(&core_interface_),
|
| + url_request_info_interface_(&core_interface_, &var_interface_),
|
| + url_response_info_interface_(&core_interface_, &var_interface_) {
|
| + FakeInstanceResource* instance_resource = new FakeInstanceResource;
|
| + instance_resource->server_template = &server_template_;
|
| + instance_ = CREATE_RESOURCE(core_interface_.resource_manager(),
|
| + FakeInstanceResource,
|
| + instance_resource);
|
| +}
|
| +
|
| +FakePepperInterfaceURLLoader::~FakePepperInterfaceURLLoader() {
|
| + core_interface_.ReleaseResource(instance_);
|
| +}
|
| +
|
| +nacl_io::CoreInterface* FakePepperInterfaceURLLoader::GetCoreInterface() {
|
| + return &core_interface_;
|
| +}
|
| +
|
| +nacl_io::VarInterface* FakePepperInterfaceURLLoader::GetVarInterface() {
|
| + return &var_interface_;
|
| +}
|
| +
|
| +nacl_io::URLLoaderInterface*
|
| +FakePepperInterfaceURLLoader::GetURLLoaderInterface() {
|
| + return &url_loader_interface_;
|
| +}
|
| +
|
| +nacl_io::URLRequestInfoInterface*
|
| +FakePepperInterfaceURLLoader::GetURLRequestInfoInterface() {
|
| + return &url_request_info_interface_;
|
| +}
|
| +
|
| +nacl_io::URLResponseInfoInterface*
|
| +FakePepperInterfaceURLLoader::GetURLResponseInfoInterface() {
|
| + return &url_response_info_interface_;
|
| +}
|
|
|