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_; |
+} |