summaryrefslogtreecommitdiffstats
path: root/native_client_sdk
diff options
context:
space:
mode:
authorbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-19 04:05:22 +0000
committerbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-19 04:05:22 +0000
commitb3c1be96db0c7b8b0dfc52a8556e6459feefe62b (patch)
tree2710f0ff31eee0e08f842c60d53476326f15f903 /native_client_sdk
parentb796a770148eabf41696278fb339437ae96112a0 (diff)
downloadchromium_src-b3c1be96db0c7b8b0dfc52a8556e6459feefe62b.zip
chromium_src-b3c1be96db0c7b8b0dfc52a8556e6459feefe62b.tar.gz
chromium_src-b3c1be96db0c7b8b0dfc52a8556e6459feefe62b.tar.bz2
[NaCl SDK] Add HTTP mount.
BUG=156781 R=noelallen@chromium.org NOTRY=true Review URL: https://chromiumcodereview.appspot.com/11887021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@177823 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rw-r--r--native_client_sdk/src/examples/hello_nacl_mounts/example.js8
-rw-r--r--native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c7
-rw-r--r--native_client_sdk/src/examples/hello_nacl_mounts/index.html2
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/Makefile2
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc2
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/library.dsc3
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/mount_html5fs.cc24
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/mount_http.cc580
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/mount_http.h43
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/mount_node.h1
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/mount_node_html5fs.cc15
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/nacl_mounts.h16
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/osinttypes.h34
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/path.cc7
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/pepper/all_interfaces.h23
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/pepper_interface.cc14
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/pepper_interface.h12
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc14
18 files changed, 771 insertions, 36 deletions
diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/example.js b/native_client_sdk/src/examples/hello_nacl_mounts/example.js
index c2376ea..c355b4d 100644
--- a/native_client_sdk/src/examples/hello_nacl_mounts/example.js
+++ b/native_client_sdk/src/examples/hello_nacl_mounts/example.js
@@ -140,6 +140,14 @@ function startsWith(s, prefix) {
function logMessage(msg) {
var logEl = document.getElementById('log');
+
+ // Perform some basic escaping.
+ msg = msg.replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&apos;');
+
logEl.innerHTML += msg + '<br>';
}
diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c b/native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c
index bf5ff77..63543d7 100644
--- a/native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c
+++ b/native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c
@@ -283,6 +283,13 @@ static PP_Bool Instance_DidCreate(PP_Instance instance,
0, /* mountflags */
"type=PERSISTENT,expected_size=1048576"); /* data */
+ mount(
+ "", /* source. Use relative URL */
+ "/http", /* target */
+ "httpfs", /* filesystemtype */
+ 0, /* mountflags */
+ ""); /* data */
+
pthread_create(&g_handle_message_thread, NULL, &HandleMessageThread, NULL);
InitializeMessageQueue();
diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/index.html b/native_client_sdk/src/examples/hello_nacl_mounts/index.html
index 2b31ba8..2f9761c 100644
--- a/native_client_sdk/src/examples/hello_nacl_mounts/index.html
+++ b/native_client_sdk/src/examples/hello_nacl_mounts/index.html
@@ -32,6 +32,8 @@ found in the LICENSE file.
is non-persistent.</li>
<li><i>/persistent</i> a persistent storage area. Any data written
here can be read back after Chrome is restarted.</li>
+ <li><i>/http</i> a mount that can read from a URL. Try reading from
+ /http/index.html.</li>
<li><i>/dev</i> a mount containing some utility files. /dev/null,
/dev/zero, etc.</li>
</ol>
diff --git a/native_client_sdk/src/libraries/nacl_mounts/Makefile b/native_client_sdk/src/libraries/nacl_mounts/Makefile
index 31daf35..5597f0c 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/Makefile
+++ b/native_client_sdk/src/libraries/nacl_mounts/Makefile
@@ -42,7 +42,7 @@ TARGET=nacl_mounts
#
SOURCES:=kernel_handle.cc kernel_intercept.cc kernel_object.cc kernel_proxy.cc
SOURCES+=kernel_wrap_glibc.cc kernel_wrap_newlib.cc kernel_wrap_win.cc
-SOURCES+=mount.cc mount_dev.cc mount_html5fs.cc mount_mem.cc
+SOURCES+=mount.cc mount_dev.cc mount_html5fs.cc mount_http.cc mount_mem.cc
SOURCES+=mount_node.cc mount_node_dir.cc mount_node_html5fs.cc mount_node_mem.cc
SOURCES+=nacl_mounts.cc path.cc pepper_interface.cc real_pepper_interface.cc
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc
index a693598..745e375 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc
@@ -14,6 +14,7 @@
#include "nacl_mounts/mount.h"
#include "nacl_mounts/mount_dev.h"
#include "nacl_mounts/mount_html5fs.h"
+#include "nacl_mounts/mount_http.h"
#include "nacl_mounts/mount_mem.h"
#include "nacl_mounts/mount_node.h"
#include "nacl_mounts/osstat.h"
@@ -48,6 +49,7 @@ void KernelProxy::Init(PepperInterface* ppapi) {
factories_["memfs"] = MountMem::Create<MountMem>;
factories_["dev"] = MountDev::Create<MountDev>;
factories_["html5fs"] = MountHtml5Fs::Create<MountHtml5Fs>;
+ factories_["httpfs"] = MountHttp::Create<MountHttp>;
// Create memory mount at root
StringMap_t smap;
diff --git a/native_client_sdk/src/libraries/nacl_mounts/library.dsc b/native_client_sdk/src/libraries/nacl_mounts/library.dsc
index 1d5ca15..83e95eb 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/library.dsc
+++ b/native_client_sdk/src/libraries/nacl_mounts/library.dsc
@@ -22,6 +22,7 @@
"mount.cc",
"mount_dev.cc",
"mount_html5fs.cc",
+ "mount_http.cc",
"mount_mem.cc",
"mount_node.cc",
"mount_node_dir.cc",
@@ -45,6 +46,7 @@
"mount.h",
"mount_dev.h",
"mount_html5fs.h",
+ "mount_http.h",
"mount_mem.h",
"mount_node_dir.h",
"mount_node.h",
@@ -52,6 +54,7 @@
"mount_node_mem.h",
"nacl_mounts.h",
"osdirent.h",
+ "osinttypes.h",
"osstat.h",
"ostypes.h",
"path.h",
diff --git a/native_client_sdk/src/libraries/nacl_mounts/mount_html5fs.cc b/native_client_sdk/src/libraries/nacl_mounts/mount_html5fs.cc
index a417b6a..b8aed8a 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/mount_html5fs.cc
+++ b/native_client_sdk/src/libraries/nacl_mounts/mount_html5fs.cc
@@ -69,18 +69,16 @@ int MountHtml5Fs::Mkdir(const Path& path, int permissions) {
return -1;
}
- PP_Resource fileref_resource = ppapi()->GetFileRefInterface()->Create(
- filesystem_resource_, path.Join().c_str());
- if (!fileref_resource) {
+ ScopedResource fileref_resource(
+ ppapi(), ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
+ path.Join().c_str()));
+ if (!fileref_resource.pp_resource()) {
errno = EINVAL;
return -1;
}
- ScopedResource scoped_resource(ppapi_, fileref_resource,
- ScopedResource::NoAddRef());
-
int32_t result = ppapi()->GetFileRefInterface()->MakeDirectory(
- fileref_resource, PP_FALSE, PP_BlockUntilComplete());
+ fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete());
if (result != PP_OK) {
errno = PPErrorToErrno(result);
return -1;
@@ -99,18 +97,16 @@ int MountHtml5Fs::Remove(const Path& path) {
return -1;
}
- PP_Resource fileref_resource = ppapi()->GetFileRefInterface()->Create(
- filesystem_resource_, path.Join().c_str());
- if (!fileref_resource) {
+ ScopedResource fileref_resource(
+ ppapi(), ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
+ path.Join().c_str()));
+ if (!fileref_resource.pp_resource()) {
errno = EINVAL;
return -1;
}
- ScopedResource scoped_resource(ppapi_, fileref_resource,
- ScopedResource::NoAddRef());
-
int32_t result = ppapi()->GetFileRefInterface()->Delete(
- fileref_resource,
+ fileref_resource.pp_resource(),
PP_BlockUntilComplete());
if (result != PP_OK) {
errno = PPErrorToErrno(result);
diff --git a/native_client_sdk/src/libraries/nacl_mounts/mount_http.cc b/native_client_sdk/src/libraries/nacl_mounts/mount_http.cc
new file mode 100644
index 0000000..0cede9c
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/mount_http.cc
@@ -0,0 +1,580 @@
+/* Copyright (c) 2012 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 "nacl_mounts/mount_http.h"
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <ppapi/c/pp_errors.h>
+#include <stdio.h>
+#include <string.h>
+#include <vector>
+#include "nacl_mounts/osinttypes.h"
+#include "utils/auto_lock.h"
+
+#if defined(WIN32)
+#define snprintf _snprintf
+#endif
+
+
+namespace {
+
+// If we're attempting to read a partial request, but the server returns a full
+// request, we need to read all of the data up to the start of our partial
+// request into a dummy buffer. This is the maximum size of that buffer.
+static const size_t MAX_READ_BUFFER_SIZE = 64 * 1024;
+static const int32_t STATUSCODE_OK = 200;
+static const int32_t STATUSCODE_PARTIAL_CONTENT = 206;
+
+std::string NormalizeHeaderKey(const std::string& s) {
+ // Capitalize the first letter and any letter following a hyphen:
+ // e.g. ACCEPT-ENCODING -> Accept-Encoding
+ std::string result;
+ bool upper = true;
+ for (size_t i = 0; i < s.length(); ++i) {
+ char c = s[i];
+ result += upper ? toupper(c) : tolower(c);
+ upper = c == '-';
+ }
+
+ return result;
+}
+
+StringMap_t ParseHeaders(const char* headers, int32_t headers_length) {
+ enum State {
+ FINDING_KEY,
+ SKIPPING_WHITESPACE,
+ FINDING_VALUE,
+ };
+
+ StringMap_t result;
+ std::string key;
+ std::string value;
+
+ State state = FINDING_KEY;
+ const char* start = headers;
+ for (int i = 0; i < headers_length; ++i) {
+ switch (state) {
+ case FINDING_KEY:
+ if (headers[i] == ':') {
+ // Found key.
+ key.assign(start, &headers[i] - start);
+ key = NormalizeHeaderKey(key);
+ state = SKIPPING_WHITESPACE;
+ }
+ break;
+
+ case SKIPPING_WHITESPACE:
+ if (headers[i] == ' ') {
+ // Found whitespace, keep going...
+ break;
+ }
+
+ // Found a non-whitespace, mark this as the start of the value.
+ start = &headers[i + 1];
+ state = FINDING_VALUE;
+ // Fallthrough to start processing value without incrementing i.
+
+ case FINDING_VALUE:
+ if (headers[i] == '\n') {
+ // Found value.
+ value.assign(start, &headers[i] - start);
+ result[key] = value;
+
+ start = &headers[i + 1];
+ state = FINDING_KEY;
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+bool ParseContentLength(const StringMap_t& headers, size_t* content_length) {
+ StringMap_t::const_iterator iter = headers.find("Content-Length");
+ if (iter == headers.end())
+ return false;
+
+ *content_length = strtoul(iter->second.c_str(), NULL, 10);
+ return true;
+}
+
+bool ParseContentRange(const StringMap_t& headers, size_t* read_start,
+ size_t* read_end, size_t* entity_length) {
+ StringMap_t::const_iterator iter = headers.find("Content-Range");
+ if (iter == headers.end())
+ return false;
+
+ // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last
+ // value is the entity length, which can potentially be * (i.e. unknown).
+ int read_start_int;
+ int read_end_int;
+ int entity_length_int;
+ int result = sscanf(iter->second.c_str(), "bytes %"SCNuS"-%"SCNuS"/%"SCNuS,
+ &read_start_int, &read_end_int, &entity_length_int);
+
+ if (result == 2) {
+ *read_start = read_start_int;
+ *read_end = read_end_int;
+ *entity_length = 0;
+ return true;
+ } else if (result == 3) {
+ *read_start = read_start_int;
+ *read_end = read_end_int;
+ *entity_length = entity_length_int;
+ return true;
+ }
+
+ return false;
+}
+
+class MountNodeHttp : public MountNode {
+ public:
+ virtual int FSync();
+ virtual int GetDents(size_t offs, struct dirent* pdir, size_t count);
+ virtual int GetStat(struct stat* stat);
+ virtual int Read(size_t offs, void* buf, size_t count);
+ virtual int Truncate(size_t size);
+ virtual int Write(size_t offs, const void* buf, size_t count);
+ virtual size_t GetSize();
+
+ protected:
+ MountNodeHttp(Mount* mount, int ino, int dev, const std::string& url);
+ virtual bool Init(int mode, short uid, short gid);
+ virtual int Close();
+
+ private:
+ bool OpenUrl(const char* method,
+ StringMap_t* request_headers,
+ PP_Resource* out_loader,
+ PP_Resource* out_request,
+ PP_Resource* out_response,
+ int32_t* out_statuscode,
+ StringMap_t* out_response_headers);
+
+ std::string url_;
+ std::vector<char> buffer_;
+
+ friend class ::MountHttp;
+};
+
+int MountNodeHttp::FSync() {
+ errno = ENOSYS;
+ return -1;
+}
+
+int MountNodeHttp::GetDents(size_t offs, struct dirent* pdir, size_t count) {
+ errno = ENOSYS;
+ return -1;
+}
+
+int MountNodeHttp::GetStat(struct stat* stat) {
+ AutoLock lock(&lock_);
+
+ StringMap_t headers;
+ PP_Resource loader;
+ PP_Resource request;
+ PP_Resource response;
+ int32_t statuscode;
+ StringMap_t response_headers;
+ if (!OpenUrl("HEAD", &headers, &loader, &request, &response, &statuscode,
+ &response_headers)) {
+ // errno is already set by OpenUrl.
+ return -1;
+ }
+
+ ScopedResource scoped_loader(mount_->ppapi(), loader);
+ ScopedResource scoped_request(mount_->ppapi(), request);
+ ScopedResource scoped_response(mount_->ppapi(), response);
+
+ // Fill in known info here.
+ memcpy(stat, &stat_, sizeof(stat_));
+
+ size_t entity_length;
+ if (ParseContentLength(response_headers, &entity_length))
+ stat->st_size = static_cast<off_t>(entity_length);
+ else
+ stat->st_size = 0;
+
+ stat->st_atime = 0; // TODO(binji): Use "Last-Modified".
+ stat->st_mtime = 0;
+ stat->st_ctime = 0;
+
+ return 0;
+}
+
+int MountNodeHttp::Read(size_t offs, void* buf, size_t count) {
+ AutoLock lock(&lock_);
+ StringMap_t headers;
+
+ char buffer[100];
+ // Range request is inclusive: 0-99 returns 100 bytes.
+ snprintf(&buffer[0], sizeof(buffer), "bytes=%"PRIuS"-%"PRIuS,
+ offs, offs + count - 1);
+ headers["Range"] = buffer;
+
+ PP_Resource loader;
+ PP_Resource request;
+ PP_Resource response;
+ int32_t statuscode;
+ StringMap_t response_headers;
+ if (!OpenUrl("GET", &headers, &loader, &request, &response, &statuscode,
+ &response_headers)) {
+ // errno is already set by OpenUrl.
+ return 0;
+ }
+
+ PepperInterface* ppapi = mount_->ppapi();
+ ScopedResource scoped_loader(ppapi, loader);
+ ScopedResource scoped_request(ppapi, request);
+ ScopedResource scoped_response(ppapi, response);
+
+ size_t read_start = 0;
+ if (statuscode == STATUSCODE_OK) {
+ // No partial result, read everything starting from the part we care about.
+ size_t content_length;
+ if (ParseContentLength(response_headers, &content_length)) {
+ if (offs >= content_length) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ // Clamp count, if trying to read past the end of the file.
+ if (offs + count > content_length) {
+ count = content_length - offs;
+ }
+ }
+ } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) {
+ // Determine from the headers where we are reading.
+ size_t read_end;
+ size_t entity_length;
+ if (ParseContentRange(response_headers, &read_start, &read_end,
+ &entity_length)) {
+ if (read_start > offs || read_start > read_end) {
+ // Shouldn't happen.
+ errno = EINVAL;
+ return 0;
+ }
+
+ // Clamp count, if trying to read past the end of the file.
+ count = std::min(read_end - read_start, count);
+ } else {
+ // Partial Content without Content-Range. Assume that the server gave us
+ // exactly what we asked for. This can happen even when the server
+ // returns 200 -- the cache may return 206 in this case, but not modify
+ // the headers.
+ read_start = offs;
+ }
+ }
+
+ URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface();
+
+ size_t bytes_to_read;
+ int32_t bytes_read;
+ while (read_start < offs) {
+ if (!buffer_.size()) {
+ buffer_.resize(std::min(offs - read_start, MAX_READ_BUFFER_SIZE));
+ }
+
+ // We aren't yet at the location where we want to start reading. Read into
+ // our dummy buffer until then.
+ bytes_to_read = std::min(offs - read_start, buffer_.size());
+ bytes_read = loader_interface->ReadResponseBody(
+ loader, buffer_.data(), bytes_to_read, PP_BlockUntilComplete());
+
+ if (bytes_read < 0) {
+ errno = PPErrorToErrno(bytes_read);
+ return 0;
+ }
+
+ assert(bytes_read <= bytes_to_read);
+ read_start += bytes_read;
+ }
+
+ // At the read start, now we can read into the correct buffer.
+ char* out_buffer = static_cast<char*>(buf);
+ bytes_to_read = count;
+ while (bytes_to_read > 0) {
+ bytes_read = loader_interface->ReadResponseBody(
+ loader, out_buffer, bytes_to_read, PP_BlockUntilComplete());
+
+ if (bytes_read == 0) {
+ // This is not an error -- it may just be that we were trying to read
+ // more data than exists.
+ return count - bytes_to_read;
+ }
+
+ if (bytes_read < 0) {
+ errno = PPErrorToErrno(bytes_read);
+ return count - bytes_to_read;
+ }
+
+ assert(bytes_read <= bytes_to_read);
+ bytes_to_read -= bytes_read;
+ out_buffer += bytes_read;
+ }
+
+ return count;
+}
+
+int MountNodeHttp::Truncate(size_t size) {
+ errno = ENOSYS;
+ return -1;
+}
+
+int MountNodeHttp::Write(size_t offs, const void* buf, size_t count) {
+ // TODO(binji): supprt POST?
+ errno = ENOSYS;
+ return -1;
+}
+
+size_t MountNodeHttp::GetSize() {
+ struct stat stat;
+ if (GetStat(&stat) == -1) {
+ // errno is already set by GetStat.
+ return -1;
+ }
+
+ return stat.st_size;
+}
+
+MountNodeHttp::MountNodeHttp(Mount* mount, int ino, int dev,
+ const std::string& url)
+ : MountNode(mount, ino, dev),
+ url_(url) {
+}
+
+bool MountNodeHttp::Init(int mode, short uid, short gid) {
+ if (!MountNode::Init(mode, uid, gid)) {
+ return false;
+ }
+
+ struct stat stat;
+ if (GetStat(&stat) == -1) {
+ return false;
+ }
+
+ memcpy(&stat_, &stat, sizeof(stat_));
+
+ return true;
+}
+
+int MountNodeHttp::Close() {
+ return 0;
+}
+
+bool MountNodeHttp::OpenUrl(const char* method,
+ StringMap_t* request_headers,
+ PP_Resource* out_loader,
+ PP_Resource* out_request,
+ PP_Resource* out_response,
+ int32_t* out_statuscode,
+ StringMap_t* out_response_headers) {
+ // Assume lock_ is already held.
+
+ PepperInterface* ppapi = mount_->ppapi();
+
+ MountHttp* mount_http = static_cast<MountHttp*>(mount_);
+ ScopedResource request(ppapi,
+ mount_http->MakeUrlRequestInfo(url_, method,
+ request_headers));
+ if (!request.pp_resource()) {
+ errno = EINVAL;
+ return false;
+ }
+
+ URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface();
+ URLResponseInfoInterface* response_interface =
+ ppapi->GetURLResponseInfoInterface();
+ VarInterface* var_interface = ppapi->GetVarInterface();
+
+ ScopedResource loader(ppapi, loader_interface->Create(ppapi->GetInstance()));
+ if (!loader.pp_resource()) {
+ errno = EINVAL;
+ return false;
+ }
+
+ int32_t result = loader_interface->Open(
+ loader.pp_resource(), request.pp_resource(), PP_BlockUntilComplete());
+ if (result != PP_OK) {
+ errno = PPErrorToErrno(result);
+ return false;
+ }
+
+ ScopedResource response(
+ ppapi,
+ loader_interface->GetResponseInfo(loader.pp_resource()));
+ if (!response.pp_resource()) {
+ errno = EINVAL;
+ return false;
+ }
+
+ // Get response statuscode.
+ PP_Var statuscode = response_interface->GetProperty(
+ response.pp_resource(),
+ PP_URLRESPONSEPROPERTY_STATUSCODE);
+
+ if (statuscode.type != PP_VARTYPE_INT32) {
+ errno = EINVAL;
+ return false;
+ }
+
+ *out_statuscode = statuscode.value.as_int;
+
+ // Only accept OK or Partial Content.
+ if (*out_statuscode != STATUSCODE_OK &&
+ *out_statuscode != STATUSCODE_PARTIAL_CONTENT) {
+ errno = EINVAL;
+ return false;
+ }
+
+ // Get response headers.
+ PP_Var response_headers_var = response_interface->GetProperty(
+ response.pp_resource(),
+ PP_URLRESPONSEPROPERTY_HEADERS);
+
+ uint32_t response_headers_length;
+ const char* response_headers_str = var_interface->VarToUtf8(
+ response_headers_var,
+ &response_headers_length);
+
+ *out_loader = loader.Release();
+ *out_request = request.Release();
+ *out_response = response.Release();
+ *out_response_headers = ParseHeaders(response_headers_str,
+ response_headers_length);
+
+ return true;
+}
+
+
+} // namespace
+
+MountNode *MountHttp::Open(const Path& path, int mode) {
+ assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
+
+ std::string url = url_root_ + (path.IsAbsolute() ?
+ path.Range(1, path.Size()) :
+ path.Join());
+
+ const int ino = 1;
+ const int USR_ID = 1001;
+ const int GRP_ID = 1002;
+ MountNodeHttp* node = new MountNodeHttp(this, ino, dev_, url);
+ if (!node->Init(mode, USR_ID, GRP_ID)) {
+ node->Release();
+ return NULL;
+ }
+
+ return node;
+}
+
+int MountHttp::Close(MountNode* node) {
+ AutoLock lock(&lock_);
+ node->Close();
+ node->Release();
+ return 0;
+}
+
+int MountHttp::Unlink(const Path& path) {
+ errno = ENOSYS;
+ return -1;
+}
+
+int MountHttp::Mkdir(const Path& path, int permissions) {
+ errno = ENOSYS;
+ return -1;
+}
+
+int MountHttp::Rmdir(const Path& path) {
+ errno = ENOSYS;
+ return -1;
+}
+
+int MountHttp::Remove(const Path& path) {
+ errno = ENOSYS;
+ return -1;
+}
+
+PP_Resource MountHttp::MakeUrlRequestInfo(
+ const std::string& url,
+ const char* method,
+ StringMap_t* additional_headers) {
+ URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface();
+ VarInterface* var_interface = ppapi_->GetVarInterface();
+
+ PP_Resource request_info = interface->Create(ppapi_->GetInstance());
+ if (!request_info)
+ return 0;
+
+ interface->SetProperty(
+ request_info, PP_URLREQUESTPROPERTY_URL,
+ var_interface->VarFromUtf8(url.c_str(), url.length()));
+ interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_METHOD,
+ var_interface->VarFromUtf8(method, strlen(method)));
+ interface->SetProperty(request_info,
+ PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
+ PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE));
+ interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
+ PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE));
+
+ // Merge the mount headers with the request headers. If the field is already
+ // set it |additional_headers|, don't use the one from headers_.
+ for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end();
+ ++iter) {
+ const std::string& key = NormalizeHeaderKey(iter->first);
+ if (additional_headers->find(key) == additional_headers->end()) {
+ additional_headers->insert(std::make_pair(key, iter->second));
+ }
+ }
+
+ // Join the headers into one string.
+ std::string headers;
+ for (StringMap_t::iterator iter = additional_headers->begin();
+ iter != additional_headers->end(); ++iter) {
+ headers += iter->first + ": " + iter->second + '\n';
+ }
+
+ interface->SetProperty(
+ request_info, PP_URLREQUESTPROPERTY_HEADERS,
+ var_interface->VarFromUtf8(headers.c_str(), headers.length()));
+
+ return request_info;
+}
+
+MountHttp::MountHttp()
+ : allow_cors_(false),
+ allow_credentials_(false) {
+}
+
+bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
+ if (!Mount::Init(dev, args, ppapi))
+ return false;
+
+ // Parse mount args.
+ for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) {
+ if (iter->first == "SOURCE") {
+ url_root_ = iter->second;
+
+ // Make sure url_root_ ends with a slash.
+ if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') {
+ url_root_ += '/';
+ }
+ } else if (iter->first == "allow_cross_origin_requests") {
+ allow_cors_ = iter->second == "true";
+ } else if (iter->first == "allow_credentials") {
+ allow_credentials_ = iter->second == "true";
+ } else {
+ // Assume it is a header to pass to an HTTP request.
+ headers_[NormalizeHeaderKey(iter->first)] = iter->second;
+ }
+ }
+
+ return true;
+}
+
+void MountHttp::Destroy() {
+}
diff --git a/native_client_sdk/src/libraries/nacl_mounts/mount_http.h b/native_client_sdk/src/libraries/nacl_mounts/mount_http.h
new file mode 100644
index 0000000..6ba47b2
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/mount_http.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012 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.
+ */
+
+#ifndef LIBRARIES_NACL_MOUNTS_MOUNT_HTTP_H_
+#define LIBRARIES_NACL_MOUNTS_MOUNT_HTTP_H_
+
+#include <string>
+#include "nacl_mounts/mount.h"
+#include "nacl_mounts/pepper_interface.h"
+
+class MountNode;
+
+class MountHttp : public Mount {
+ public:
+ virtual MountNode *Open(const Path& path, int mode);
+ virtual int Close(MountNode* node);
+ virtual int Unlink(const Path& path);
+ virtual int Mkdir(const Path& path, int permissions);
+ virtual int Rmdir(const Path& path);
+ virtual int Remove(const Path& path);
+
+ PP_Resource MakeUrlRequestInfo(const std::string& url,
+ const char* method,
+ StringMap_t* additional_headers);
+
+ protected:
+ MountHttp();
+
+ virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi);
+ virtual void Destroy();
+
+ private:
+ std::string url_root_;
+ StringMap_t headers_;
+ bool allow_cors_;
+ bool allow_credentials_;
+
+ friend class Mount;
+};
+
+#endif // LIBRARIES_NACL_MOUNTS_MOUNT_HTTP_H_
diff --git a/native_client_sdk/src/libraries/nacl_mounts/mount_node.h b/native_client_sdk/src/libraries/nacl_mounts/mount_node.h
index 2592c8e..a11a6a9 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/mount_node.h
+++ b/native_client_sdk/src/libraries/nacl_mounts/mount_node.h
@@ -65,6 +65,7 @@ protected:
friend class MountDev;
friend class MountHtml5Fs;
+ friend class MountHttp;
friend class MountMem;
friend class MountNodeDir;
};
diff --git a/native_client_sdk/src/libraries/nacl_mounts/mount_node_html5fs.cc b/native_client_sdk/src/libraries/nacl_mounts/mount_node_html5fs.cc
index 648b94b..7652946 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/mount_node_html5fs.cc
+++ b/native_client_sdk/src/libraries/nacl_mounts/mount_node_html5fs.cc
@@ -76,24 +76,21 @@ int MountNodeHtml5Fs::GetDents(size_t offs, struct dirent* pdir, size_t size) {
return -1;
}
- PP_Resource directory_reader_resource =
- mount_->ppapi()->GetDirectoryReaderInterface()->Create(fileref_resource_);
- if (!directory_reader_resource) {
+ ScopedResource directory_reader(
+ mount_->ppapi(),
+ mount_->ppapi()->GetDirectoryReaderInterface()->Create(
+ fileref_resource_));
+ if (!directory_reader.pp_resource()) {
errno = ENOSYS;
return -1;
}
- ScopedResource scoped_directory_reader(
- mount_->ppapi(),
- directory_reader_resource,
- ScopedResource::NoAddRef());
-
std::vector<struct dirent> dirents;
PP_DirectoryEntry_Dev directory_entry = {0};
while (1) {
int32_t result =
mount_->ppapi()->GetDirectoryReaderInterface()->GetNextEntry(
- directory_reader_resource, &directory_entry,
+ directory_reader.pp_resource(), &directory_entry,
PP_BlockUntilComplete());
if (result != PP_OK) {
errno = PPErrorToErrno(result);
diff --git a/native_client_sdk/src/libraries/nacl_mounts/nacl_mounts.h b/native_client_sdk/src/libraries/nacl_mounts/nacl_mounts.h
index 6a09c429..b70957f 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/nacl_mounts.h
+++ b/native_client_sdk/src/libraries/nacl_mounts/nacl_mounts.h
@@ -78,6 +78,22 @@ void nacl_mounts_init_ppapi(PP_Instance instance,
* "expected_size": The expected file-system size. Note that this does
* not request quota -- you must do that from JavaScript.
*
+ * "httpfs": A filesystem that reads from a URL via HTTP.
+ * source: The root URL to read from. All paths read from this filesystem
+ * will be appended to this root.
+ * e.g. If source == "http://example.com/path", reading from
+ * "foo/bar.txt" will attempt to read from the URL
+ * "http://example.com/path/foo/bar.txt".
+ * data: A string of parameters:
+ * "allow_cross_origin_request": If "true", then reads from this
+ * filesystem will follow the CORS standard for cross-origin requests.
+ * See http://www.w3.org/TR/access-control.
+ * "allow_credentials": If "true", credentials are sent with cross-origin
+ * requests. If false, no credentials are sent with the request and
+ * cookies are ignored in the response.
+ * All other key/value pairs are assumed to be headers to use with
+ * HTTP requests.
+ *
*
* @param[in] source Depends on the filesystem type. See above.
* @param[in] target The absolute path to mount the filesystem.
diff --git a/native_client_sdk/src/libraries/nacl_mounts/osinttypes.h b/native_client_sdk/src/libraries/nacl_mounts/osinttypes.h
new file mode 100644
index 0000000..e6705f0
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/osinttypes.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2012 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.
+ */
+#ifndef NACL_MOUNTS_OSINTTYPES_H_
+#define NACL_MOUNTS_OSINTTYPES_H_
+
+// Define printf/scanf format strings for size_t.
+
+#if defined(WIN32)
+
+#if !defined(PRIuS)
+#define PRIuS "Iu"
+#endif
+
+#if !defined(SCNuS)
+#define SCNuS "Iu"
+#endif
+
+#elif defined(__native_client__)
+
+#include <inttypes.h>
+
+#if !defined(PRIuS)
+#define PRIuS "zu"
+#endif
+
+#if !defined(SCNuS)
+#define SCNuS "zu"
+#endif
+
+#endif // defined(__native_client__)
+
+#endif // NACL_MOUNTS_OSINTTYPES_H_
diff --git a/native_client_sdk/src/libraries/nacl_mounts/path.cc b/native_client_sdk/src/libraries/nacl_mounts/path.cc
index 47d4f45..63978b7 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/path.cc
+++ b/native_client_sdk/src/libraries/nacl_mounts/path.cc
@@ -146,14 +146,17 @@ std::string Path::Range(const StringArray_t& paths, size_t start, size_t end) {
if (end > paths.size()) end = paths.size();
+ // If this is an absolute path, paths[0] == "/". In this case, we don't want
+ // to add an additional / separator.
if (start == 0 && end > 0 && paths[0] == "/") {
- if (end == 1) return std::string("/");
+ out_path += "/";
index++;
}
for (; index < end; index++) {
- if (index) out_path += "/";
out_path += paths[index];
+ if (index < end - 1)
+ out_path += "/";
}
return out_path;
diff --git a/native_client_sdk/src/libraries/nacl_mounts/pepper/all_interfaces.h b/native_client_sdk/src/libraries/nacl_mounts/pepper/all_interfaces.h
index 1dd46b1..0851dc4 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/pepper/all_interfaces.h
+++ b/native_client_sdk/src/libraries/nacl_mounts/pepper/all_interfaces.h
@@ -69,3 +69,26 @@ BEGIN_INTERFACE(VarInterface, PPB_Var, PPB_VAR_INTERFACE)
METHOD2(VarInterface, struct PP_Var, VarFromUtf8, const char *, uint32_t)
METHOD2(VarInterface, const char*, VarToUtf8, PP_Var, uint32_t*)
END_INTERFACE(VarInterface, PPB_Var)
+
+BEGIN_INTERFACE(URLLoaderInterface, PPB_URLLoader, PPB_URLLOADER_INTERFACE)
+ METHOD1(URLLoaderInterface, PP_Resource, Create, PP_Instance)
+ METHOD3(URLLoaderInterface, int32_t, Open, PP_Resource, PP_Resource,
+ PP_CompletionCallback)
+ METHOD1(URLLoaderInterface, PP_Resource, GetResponseInfo, PP_Resource)
+ METHOD4(URLLoaderInterface, int32_t, ReadResponseBody, PP_Resource, void*,
+ int32_t, PP_CompletionCallback)
+ METHOD1(URLLoaderInterface, void, Close, PP_Resource)
+END_INTERFACE(URLLoaderInterface, PPB_URLLoader)
+
+BEGIN_INTERFACE(URLRequestInfoInterface, PPB_URLRequestInfo,
+ PPB_URLREQUESTINFO_INTERFACE)
+ METHOD1(URLRequestInfoInterface, PP_Resource, Create, PP_Instance)
+ METHOD3(URLRequestInfoInterface, PP_Bool, SetProperty, PP_Resource,
+ PP_URLRequestProperty, PP_Var)
+END_INTERFACE(URLRequestInfoInterface, PPB_URLRequestInfo)
+
+BEGIN_INTERFACE(URLResponseInfoInterface, PPB_URLResponseInfo,
+ PPB_URLRESPONSEINFO_INTERFACE)
+ METHOD2(URLResponseInfoInterface, PP_Var, GetProperty, PP_Resource,
+ PP_URLResponseProperty)
+END_INTERFACE(URLResponseInfoInterface, PPB_URLResponseInfo)
diff --git a/native_client_sdk/src/libraries/nacl_mounts/pepper_interface.cc b/native_client_sdk/src/libraries/nacl_mounts/pepper_interface.cc
index 16c6b8e..51dff4e 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/pepper_interface.cc
+++ b/native_client_sdk/src/libraries/nacl_mounts/pepper_interface.cc
@@ -9,17 +9,17 @@
ScopedResource::ScopedResource(PepperInterface* ppapi, PP_Resource resource)
: ppapi_(ppapi),
resource_(resource) {
- ppapi_->AddRefResource(resource_);
}
-ScopedResource::ScopedResource(PepperInterface* ppapi, PP_Resource resource,
- NoAddRef)
- : ppapi_(ppapi),
- resource_(resource) {
+ScopedResource::~ScopedResource() {
+ if (resource_)
+ ppapi_->ReleaseResource(resource_);
}
-ScopedResource::~ScopedResource() {
- ppapi_->ReleaseResource(resource_);
+PP_Resource ScopedResource::Release() {
+ PP_Resource result = resource_;
+ resource_ = 0;
+ return result;
}
int PPErrorToErrno(int32_t err) {
diff --git a/native_client_sdk/src/libraries/nacl_mounts/pepper_interface.h b/native_client_sdk/src/libraries/nacl_mounts/pepper_interface.h
index 399c805..2c5c004 100644
--- a/native_client_sdk/src/libraries/nacl_mounts/pepper_interface.h
+++ b/native_client_sdk/src/libraries/nacl_mounts/pepper_interface.h
@@ -17,6 +17,9 @@
#include <ppapi/c/ppb_file_system.h>
#include <ppapi/c/ppb_messaging.h>
#include <ppapi/c/ppb_messaging.h>
+#include <ppapi/c/ppb_url_loader.h>
+#include <ppapi/c/ppb_url_request_info.h>
+#include <ppapi/c/ppb_url_response_info.h>
#include <ppapi/c/ppb_var.h>
#include <utils/macros.h>
@@ -79,12 +82,15 @@ class PepperInterface {
class ScopedResource {
public:
- struct NoAddRef {};
-
+ // Does not AddRef by default.
ScopedResource(PepperInterface* ppapi, PP_Resource resource);
- ScopedResource(PepperInterface* ppapi, PP_Resource resource, NoAddRef);
~ScopedResource();
+ PP_Resource pp_resource() { return resource_; }
+
+ // Return the resource without decrementing its refcount.
+ PP_Resource Release();
+
private:
PepperInterface* ppapi_;
PP_Resource resource_;
diff --git a/native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc b/native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc
index 679c3a1..ea89d42 100644
--- a/native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc
+++ b/native_client_sdk/src/libraries/nacl_mounts_test/path_test.cc
@@ -242,3 +242,17 @@ TEST(PathTest, Invalid) {
test.Append("../../../foo");
EXPECT_EQ("../foo", test.Join());
}
+
+TEST(PathTest, Range) {
+ Path p("/an/absolute/path");
+
+ // p's parts should be ["/", "an", "absolute", "path"].
+ EXPECT_EQ("/an/absolute/path", p.Range(0, 4));
+ EXPECT_EQ("an/absolute/path", p.Range(1, 4));
+ EXPECT_EQ("absolute/path", p.Range(2, 4));
+ EXPECT_EQ("path", p.Range(3, 4));
+
+ EXPECT_EQ("/an/absolute", p.Range(0, 3));
+ EXPECT_EQ("an/absolute", p.Range(1, 3));
+ EXPECT_EQ("absolute", p.Range(2, 3));
+}