summaryrefslogtreecommitdiffstats
path: root/native_client_sdk
diff options
context:
space:
mode:
authorbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-01 18:38:26 +0000
committerbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-01 18:38:26 +0000
commit60c97c7102f43ac23277db385e0cccd9d25aa1fa (patch)
tree39ecb0c274aedceb9f88e780bfadcf78b2d9ec30 /native_client_sdk
parenta44f10db94596a2bb5f0c03487bb98bb7b1696cc (diff)
downloadchromium_src-60c97c7102f43ac23277db385e0cccd9d25aa1fa.zip
chromium_src-60c97c7102f43ac23277db385e0cccd9d25aa1fa.tar.gz
chromium_src-60c97c7102f43ac23277db385e0cccd9d25aa1fa.tar.bz2
[NaCl SDK] nacl_io http mount caches content by default.
BUG=175337 R=sbc@chromium.org,noelallen@chromium.org NOTRY=true Review URL: https://chromiumcodereview.appspot.com/12354002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@185557 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_http.cc334
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_http.h3
-rw-r--r--native_client_sdk/src/libraries/nacl_io_test/example.dsc1
-rw-r--r--native_client_sdk/src/libraries/nacl_io_test/example.js11
-rw-r--r--native_client_sdk/src/libraries/nacl_io_test/index.html4
-rw-r--r--native_client_sdk/src/libraries/nacl_io_test/mock_util.h56
-rw-r--r--native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc48
-rw-r--r--native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc396
8 files changed, 654 insertions, 199 deletions
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_http.cc
index e387dba..5786422 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_http.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount_http.cc
@@ -23,8 +23,10 @@
#endif
+namespace {
+
typedef std::vector<char *> StringList_t;
-static size_t SplitString(char *str, const char *delim, StringList_t* list) {
+size_t SplitString(char *str, const char *delim, StringList_t* list) {
char *item = strtok(str, delim);
list->clear();
@@ -40,9 +42,9 @@ static size_t SplitString(char *str, const char *delim, StringList_t* list) {
// 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;
+const size_t MAX_READ_BUFFER_SIZE = 64 * 1024;
+const int32_t STATUSCODE_OK = 200;
+const int32_t STATUSCODE_PARTIAL_CONTENT = 206;
std::string NormalizeHeaderKey(const std::string& s) {
// Capitalize the first letter and any letter following a hyphen:
@@ -149,6 +151,9 @@ bool ParseContentRange(const StringMap_t& headers, size_t* read_start,
return false;
}
+} // namespace
+
+
class MountNodeHttp : public MountNode {
public:
virtual int FSync();
@@ -160,7 +165,7 @@ class MountNodeHttp : public MountNode {
virtual size_t GetSize();
protected:
- MountNodeHttp(Mount* mount, const std::string& url);
+ MountNodeHttp(Mount* mount, const std::string& url, bool cache_content);
private:
bool OpenUrl(const char* method,
@@ -171,8 +176,16 @@ class MountNodeHttp : public MountNode {
int32_t* out_statuscode,
StringMap_t* out_response_headers);
+ int DownloadToCache();
+ int ReadPartialFromCache(size_t offs, void* buf, size_t count);
+ int DownloadPartial(size_t offs, void* buf, size_t count);
+ int DownloadToBuffer(PP_Resource loader, void* buf, size_t count);
+
std::string url_;
std::vector<char> buffer_;
+
+ bool cache_content_;
+ std::vector<char> cached_data_;
friend class ::MountHttp;
};
@@ -192,7 +205,7 @@ int MountNodeHttp::GetStat(struct stat* stat) {
// Assume we need to 'HEAD' if we do not know the size, otherwise, assume
// that the information is constant. We can add a timeout if needed.
MountHttp* mount = static_cast<MountHttp*>(mount_);
- if (stat_.st_size == 0 || !mount->allow_stat_cache_) {
+ if (stat_.st_size == 0 || !mount->cache_stat_) {
StringMap_t headers;
PP_Resource loader;
PP_Resource request;
@@ -230,116 +243,16 @@ int MountNodeHttp::GetStat(struct stat* stat) {
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;
- }
+ if (cache_content_) {
+ if (cached_data_.empty()) {
+ if (DownloadToCache() < 0)
+ return -1;
}
- } 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 ReadPartialFromCache(offs, buf, count);
}
- return count;
+ return DownloadPartial(offs, buf, count);
}
int MountNodeHttp::Truncate(size_t size) {
@@ -359,9 +272,11 @@ size_t MountNodeHttp::GetSize() {
return stat_.st_size;
}
-MountNodeHttp::MountNodeHttp(Mount* mount, const std::string& url)
+MountNodeHttp::MountNodeHttp(Mount* mount, const std::string& url,
+ bool cache_content)
: MountNode(mount),
- url_(url) {
+ url_(url),
+ cache_content_(cache_content) {
}
bool MountNodeHttp::OpenUrl(const char* method,
@@ -448,6 +363,184 @@ bool MountNodeHttp::OpenUrl(const char* method,
return true;
}
+int MountNodeHttp::DownloadToCache() {
+ StringMap_t headers;
+ 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 -1;
+ }
+
+ PepperInterface* ppapi = mount_->ppapi();
+ ScopedResource scoped_loader(ppapi, loader);
+ ScopedResource scoped_request(ppapi, request);
+ ScopedResource scoped_response(ppapi, response);
+
+ size_t content_length = 0;
+ if (ParseContentLength(response_headers, &content_length)) {
+ cached_data_.resize(content_length);
+ int real_size = DownloadToBuffer(loader, cached_data_.data(),
+ content_length);
+ if (real_size < 0)
+ return -1;
+
+ stat_.st_size = real_size;
+ cached_data_.resize(real_size);
+ return real_size;
+ }
+
+ // We don't know how big the file is. Read in chunks.
+ cached_data_.resize(MAX_READ_BUFFER_SIZE);
+ char* buf = cached_data_.data();
+ size_t total_bytes_read = 0;
+ size_t bytes_to_read = MAX_READ_BUFFER_SIZE;
+ while (true) {
+ int bytes_read = DownloadToBuffer(loader, buf, bytes_to_read);
+ if (bytes_read < 0)
+ return -1;
+
+ total_bytes_read += bytes_read;
+
+ if (bytes_read < bytes_to_read) {
+ stat_.st_size = total_bytes_read;
+ cached_data_.resize(total_bytes_read);
+ return total_bytes_read;
+ }
+
+ buf += bytes_read;
+ cached_data_.resize(total_bytes_read + bytes_to_read);
+ }
+}
+
+int MountNodeHttp::ReadPartialFromCache(size_t offs, void* buf, size_t count) {
+ if (offs >= cached_data_.size()) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ count = std::min(count, cached_data_.size() - offs);
+ memcpy(buf, &cached_data_.data()[offs], count);
+ return count;
+}
+
+int MountNodeHttp::DownloadPartial(size_t offs, void* buf, size_t count) {
+ 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 -1;
+ }
+
+ 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) {
+ // If this error occurs, the server is returning bogus values.
+ errno = EINVAL;
+ return -1;
+ }
+
+ // 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;
+ }
+ }
+
+ if (read_start < offs) {
+ // We aren't yet at the location where we want to start reading. Read into
+ // our dummy buffer until then.
+ size_t bytes_to_read = offs - read_start;
+ if (buffer_.size() < bytes_to_read)
+ buffer_.resize(std::min(bytes_to_read, MAX_READ_BUFFER_SIZE));
+
+ while (bytes_to_read > 0) {
+ int32_t bytes_read = DownloadToBuffer(loader, buffer_.data(),
+ buffer_.size());
+ if (bytes_read < 0)
+ return -1;
+
+ bytes_to_read -= bytes_read;
+ }
+ }
+
+ return DownloadToBuffer(loader, buf, count);
+}
+
+int MountNodeHttp::DownloadToBuffer(PP_Resource loader, void* buf,
+ size_t count) {
+ PepperInterface* ppapi = mount_->ppapi();
+ URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface();
+
+ char* out_buffer = static_cast<char*>(buf);
+ size_t bytes_to_read = count;
+ while (bytes_to_read > 0) {
+ int32_t 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 -1;
+ }
+
+ assert(bytes_read <= bytes_to_read);
+ bytes_to_read -= bytes_read;
+ out_buffer += bytes_read;
+ }
+
+ return count;
+}
+
MountNode *MountHttp::Open(const Path& path, int mode) {
assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
@@ -461,7 +554,7 @@ MountNode *MountHttp::Open(const Path& path, int mode) {
path.Range(1, path.Size()) :
path.Join());
- MountNodeHttp* node = new MountNodeHttp(this, url);
+ MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_);
if (!node->Init(mode) || (0 != node->GetStat(NULL))) {
node->Release();
return NULL;
@@ -542,7 +635,8 @@ PP_Resource MountHttp::MakeUrlRequestInfo(
MountHttp::MountHttp()
: allow_cors_(false),
allow_credentials_(false),
- allow_stat_cache_(true) {
+ cache_stat_(true),
+ cache_content_(true) {
}
bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
@@ -568,8 +662,10 @@ bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
allow_cors_ = iter->second == "true";
} else if (iter->first == "allow_credentials") {
allow_credentials_ = iter->second == "true";
- } else if (iter->first == "allow_stat_cache") {
- allow_stat_cache_ = iter->second == "true";
+ } else if (iter->first == "cache_stat") {
+ cache_stat_ = iter->second == "true";
+ } else if (iter->first == "cache_content") {
+ cache_content_ = iter->second == "true";
} else {
// Assume it is a header to pass to an HTTP request.
headers_[NormalizeHeaderKey(iter->first)] = iter->second;
@@ -652,7 +748,7 @@ bool MountHttp::ParseManifest(char *text) {
path.Range(1, path.Size()) :
path.Join());
- MountNode* node = new MountNodeHttp(this, url);
+ MountNode* node = new MountNodeHttp(this, url, cache_content_);
node->Init(mode);
node->stat_.st_size = atoi(lenstr);
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.h b/native_client_sdk/src/libraries/nacl_io/mount_http.h
index eacfb2c..3a842a04 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_http.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_http.h
@@ -44,7 +44,8 @@ class MountHttp : public Mount {
NodeMap_t node_cache_;
bool allow_cors_;
bool allow_credentials_;
- bool allow_stat_cache_;
+ bool cache_stat_;
+ bool cache_content_;
friend class Mount;
friend class MountNodeHttp;
diff --git a/native_client_sdk/src/libraries/nacl_io_test/example.dsc b/native_client_sdk/src/libraries/nacl_io_test/example.dsc
index ea533d3..a47ed63 100644
--- a/native_client_sdk/src/libraries/nacl_io_test/example.dsc
+++ b/native_client_sdk/src/libraries/nacl_io_test/example.dsc
@@ -14,6 +14,7 @@
'kernel_proxy_mock.h',
'kernel_proxy_test.cc',
'kernel_wrap_test.cc',
+ 'mock_util.h',
'module.cc',
'mount_node_test.cc',
'mount_html5fs_test.cc',
diff --git a/native_client_sdk/src/libraries/nacl_io_test/example.js b/native_client_sdk/src/libraries/nacl_io_test/example.js
index 4e7cb0a..2d93739 100644
--- a/native_client_sdk/src/libraries/nacl_io_test/example.js
+++ b/native_client_sdk/src/libraries/nacl_io_test/example.js
@@ -11,5 +11,14 @@ function moduleDidLoad() {
function handleMessage(event) {
var logEl = document.getElementById('log');
- logEl.innerHTML += event.data + '<br>';
+ var msg = event.data;
+
+ // Perform some basic escaping.
+ msg = msg.replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&apos;');
+
+ logEl.innerHTML += msg + '\n';
}
diff --git a/native_client_sdk/src/libraries/nacl_io_test/index.html b/native_client_sdk/src/libraries/nacl_io_test/index.html
index 4e47334..424f557 100644
--- a/native_client_sdk/src/libraries/nacl_io_test/index.html
+++ b/native_client_sdk/src/libraries/nacl_io_test/index.html
@@ -13,11 +13,11 @@ found in the LICENSE file.
<script type="text/javascript" src="example.js"></script>
</head>
<body {{attrs}}>
- <h1><TITLE></h1>
+ <h1>{{title}}</h1>
<h2>Status: <code id="statusField">NO-STATUS</code></h2>
<!-- The NaCl plugin will be embedded inside the element with id "listener".
See common.js.-->
<div id="listener"></div>
- <div id="log"></div>
+ <pre id="log"></pre>
</body>
</html>
diff --git a/native_client_sdk/src/libraries/nacl_io_test/mock_util.h b/native_client_sdk/src/libraries/nacl_io_test/mock_util.h
new file mode 100644
index 0000000..84145bf
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io_test/mock_util.h
@@ -0,0 +1,56 @@
+/* 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.
+ */
+
+#ifndef LIBRARIES_NACL_IO_TEST_MOCK_UTIL_H_
+#define LIBRARIES_NACL_IO_TEST_MOCK_UTIL_H_
+
+#include <gmock/gmock.h>
+#include <ppapi/c/pp_completion_callback.h>
+#include <ppapi/c/pp_var.h>
+
+ACTION_TEMPLATE(CallCallback,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_1_VALUE_PARAMS(result)) {
+ PP_CompletionCallback callback = std::tr1::get<k>(args);
+ if (callback.func) {
+ (*callback.func)(callback.user_data, result);
+ }
+
+ // Dummy return value.
+ return 0;
+}
+
+MATCHER_P(IsEqualToVar, var, "") {
+ if (arg.type != var.type)
+ return false;
+
+ switch (arg.type) {
+ case PP_VARTYPE_BOOL:
+ return arg.value.as_bool == var.value.as_bool;
+
+ case PP_VARTYPE_INT32:
+ return arg.value.as_int == var.value.as_int;
+
+ case PP_VARTYPE_DOUBLE:
+ return arg.value.as_double == var.value.as_double;
+
+ case PP_VARTYPE_STRING:
+ return arg.value.as_id == var.value.as_id;
+
+ case PP_VARTYPE_UNDEFINED:
+ case PP_VARTYPE_NULL:
+ return true;
+
+ case PP_VARTYPE_ARRAY:
+ case PP_VARTYPE_ARRAY_BUFFER:
+ case PP_VARTYPE_DICTIONARY:
+ case PP_VARTYPE_OBJECT:
+ default:
+ // Not supported.
+ return false;
+ }
+}
+
+#endif // LIBRARIES_NACL_IO_TEST_MOCK_UTIL_H_
diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc
index 259d6ae..0814a10 100644
--- a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc
+++ b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc
@@ -9,6 +9,7 @@
#include <ppapi/c/pp_errors.h>
#include <ppapi/c/pp_instance.h>
+#include "mock_util.h"
#include "nacl_io/mount_html5fs.h"
#include "nacl_io/osdirent.h"
#include "pepper_interface_mock.h"
@@ -21,54 +22,9 @@ using ::testing::StrEq;
namespace {
-ACTION_TEMPLATE(CallCallback,
- HAS_1_TEMPLATE_PARAMS(int, k),
- AND_1_VALUE_PARAMS(result)) {
- PP_CompletionCallback callback = std::tr1::get<k>(args);
- if (callback.func) {
- (*callback.func)(callback.user_data, result);
- }
-
- // Dummy return value.
- return 0;
-}
-
-MATCHER_P(IsEqualToVar, var, "") {
- if (arg.type != var.type)
- return false;
-
- switch (arg.type) {
- case PP_VARTYPE_BOOL:
- return arg.value.as_bool == var.value.as_bool;
-
- case PP_VARTYPE_INT32:
- return arg.value.as_int == var.value.as_int;
-
- case PP_VARTYPE_DOUBLE:
- return arg.value.as_double == var.value.as_double;
-
- case PP_VARTYPE_STRING:
- return arg.value.as_id == var.value.as_id;
-
- case PP_VARTYPE_UNDEFINED:
- case PP_VARTYPE_NULL:
- return true;
-
- case PP_VARTYPE_ARRAY:
- case PP_VARTYPE_ARRAY_BUFFER:
- case PP_VARTYPE_DICTIONARY:
- case PP_VARTYPE_OBJECT:
- default:
- // Not supported.
- return false;
- }
-}
-
-
class MountHtml5FsMock : public MountHtml5Fs {
public:
- explicit MountHtml5FsMock(StringMap_t map, PepperInterfaceMock* ppapi)
- : MountHtml5Fs() {
+ MountHtml5FsMock(StringMap_t map, PepperInterfaceMock* ppapi) {
Init(1, map, ppapi);
}
diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc
index edf35cb..f117093 100644
--- a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc
+++ b/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc
@@ -11,6 +11,7 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include "mock_util.h"
#include "nacl_io/kernel_intercept.h"
#include "nacl_io/mount_http.h"
#include "nacl_io/mount_node_dir.h"
@@ -19,71 +20,76 @@
using ::testing::_;
using ::testing::DoAll;
+using ::testing::Mock;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrEq;
-class MountHttpTest : public ::testing::Test {
+
+class MountHttpMock : public MountHttp {
public:
- MountHttpTest() {
- ki_init(NULL);
+ MountHttpMock(StringMap_t map, PepperInterfaceMock* ppapi) {
+ EXPECT_TRUE(Init(1, map, ppapi));
}
- ~MountHttpTest() {
- ki_uninit();
+ ~MountHttpMock() {
+ Destroy();
}
+
+ NodeMap_t& GetMap() { return node_cache_; }
+
+ using MountHttp::ParseManifest;
+ using MountHttp::FindOrCreateDir;
};
-class MountHttpMock : public MountHttp {
+class MountHttpTest : public ::testing::Test {
public:
- MountHttpMock() : MountHttp() {};
+ MountHttpTest();
+ ~MountHttpTest();
- virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
- return MountHttp::Init(dev, args, ppapi);
- }
+ protected:
+ PepperInterfaceMock ppapi_;
+ MountHttpMock* mnt_;
- bool ParseManifest(char *txt) {
- return MountHttp::ParseManifest(txt);
- }
+ static const PP_Instance instance_ = 123;
+};
- MountNodeDir* FindOrCreateDir(const Path& path) {
- return MountHttp::FindOrCreateDir(path);
- }
+MountHttpTest::MountHttpTest()
+ : ppapi_(instance_),
+ mnt_(NULL) {
+}
- NodeMap_t& GetMap() { return node_cache_; }
+MountHttpTest::~MountHttpTest() {
+ delete mnt_;
+}
- friend class MountHttpTest;
-};
TEST_F(MountHttpTest, MountEmpty) {
- MountHttpMock mnt;
StringMap_t args;
-
- EXPECT_TRUE(mnt.Init(1, args, NULL));
+ mnt_ = new MountHttpMock(args, &ppapi_);
}
TEST_F(MountHttpTest, ParseManifest) {
- MountHttpMock mnt;
StringMap_t args;
+ mnt_ = new MountHttpMock(args, &ppapi_);
char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
- EXPECT_TRUE(mnt.Init(1, args, NULL));
- EXPECT_TRUE(mnt.ParseManifest(manifest));
+ EXPECT_TRUE(mnt_->ParseManifest(manifest));
- MountNodeDir* root = mnt.FindOrCreateDir(Path("/"));
+ MountNodeDir* root = mnt_->FindOrCreateDir(Path("/"));
EXPECT_EQ(2, root->ChildCount());
- MountNodeDir* dir = mnt.FindOrCreateDir(Path("/mydir"));
+ MountNodeDir* dir = mnt_->FindOrCreateDir(Path("/mydir"));
EXPECT_EQ(1, dir->ChildCount());
- MountNode* node = mnt.GetMap()["/mydir/foo"];
+ MountNode* node = mnt_->GetMap()["/mydir/foo"];
EXPECT_TRUE(node);
EXPECT_EQ(123, node->GetSize());
// Since these files are cached thanks to the manifest, we can open them
// without accessing the PPAPI URL API.
- MountNode* foo = mnt.Open(Path("/mydir/foo"), O_RDONLY);
- MountNode* bar = mnt.Open(Path("/thatdir/bar"), O_RDWR);
+ MountNode* foo = mnt_->Open(Path("/mydir/foo"), O_RDONLY);
+ MountNode* bar = mnt_->Open(Path("/thatdir/bar"), O_RDWR);
struct stat sfoo;
struct stat sbar;
@@ -97,3 +103,333 @@ TEST_F(MountHttpTest, ParseManifest) {
EXPECT_EQ(234, sbar.st_size);
EXPECT_EQ(S_IFREG | S_IREAD | S_IWRITE, sbar.st_mode);
}
+
+
+class MountHttpNodeTest : public MountHttpTest {
+ public:
+ MountHttpNodeTest();
+ virtual void TearDown();
+
+ void SetMountArgs(const StringMap_t& args);
+ void ExpectOpen(const char* method);
+ void ExpectHeaders(const char* headers);
+ void OpenNode();
+ void SetResponse(int status_code, const char* headers);
+ void SetResponseBody(const char* body);
+ void ResetMocks();
+
+ protected:
+ MountHttpMock* mnt_;
+ MountNode* node_;
+
+ VarInterfaceMock* var_;
+ URLLoaderInterfaceMock* loader_;
+ URLRequestInfoInterfaceMock* request_;
+ URLResponseInfoInterfaceMock* response_;
+ size_t response_body_offset_;
+
+ static const char path_[];
+ static const char rel_path_[];
+ static const PP_Resource loader_resource_ = 235;
+ static const PP_Resource request_resource_ = 236;
+ static const PP_Resource response_resource_ = 237;
+};
+
+// static
+const char MountHttpNodeTest::path_[] = "/foo";
+// static
+const char MountHttpNodeTest::rel_path_[] = "foo";
+
+MountHttpNodeTest::MountHttpNodeTest()
+ : mnt_(NULL),
+ node_(NULL) {
+}
+
+static PP_Var MakeString(PP_Resource resource) {
+ PP_Var result = { PP_VARTYPE_STRING, 0, {PP_FALSE} };
+ result.value.as_id = resource;
+ return result;
+}
+
+void MountHttpNodeTest::SetMountArgs(const StringMap_t& args) {
+ mnt_ = new MountHttpMock(args, &ppapi_);
+}
+
+void MountHttpNodeTest::ExpectOpen(const char* method) {
+ loader_ = ppapi_.GetURLLoaderInterface();
+ request_ = ppapi_.GetURLRequestInfoInterface();
+ response_ = ppapi_.GetURLResponseInfoInterface();
+ var_ = ppapi_.GetVarInterface();
+
+ ON_CALL(*request_, SetProperty(request_resource_, _, _))
+ .WillByDefault(Return(PP_TRUE));
+ ON_CALL(*var_, VarFromUtf8(_, _)).WillByDefault(Return(PP_MakeUndefined()));
+
+ EXPECT_CALL(*loader_, Create(instance_)).WillOnce(Return(loader_resource_));
+ EXPECT_CALL(*request_, Create(instance_)).WillOnce(Return(request_resource_));
+
+ PP_Var var_head = MakeString(345);
+ PP_Var var_url = MakeString(346);
+ EXPECT_CALL(*var_, VarFromUtf8(StrEq(method), _)).WillOnce(Return(var_head));
+ EXPECT_CALL(*var_, VarFromUtf8(StrEq(rel_path_), _))
+ .WillOnce(Return(var_url));
+
+#define EXPECT_SET_PROPERTY(NAME, VAR) \
+ EXPECT_CALL(*request_, SetProperty(request_resource_, NAME, VAR))
+
+ EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_URL, IsEqualToVar(var_url));
+ EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_METHOD, IsEqualToVar(var_head));
+ EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, _);
+ EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, _);
+
+#undef EXPECT_SET_PROPERTY
+
+ EXPECT_CALL(*loader_, Open(loader_resource_, request_resource_, _))
+ .WillOnce(CallCallback<2>(int32_t(PP_OK)));
+ EXPECT_CALL(*loader_, GetResponseInfo(loader_resource_))
+ .WillOnce(Return(response_resource_));
+
+ EXPECT_CALL(ppapi_, ReleaseResource(loader_resource_));
+ EXPECT_CALL(ppapi_, ReleaseResource(request_resource_));
+ EXPECT_CALL(ppapi_, ReleaseResource(response_resource_));
+}
+
+void MountHttpNodeTest::ExpectHeaders(const char* headers) {
+ PP_Var var_headers = MakeString(347);
+ var_ = ppapi_.GetVarInterface();
+ EXPECT_CALL(*var_, VarFromUtf8(StrEq(headers), _))
+ .WillOnce(Return(var_headers));
+
+ EXPECT_CALL(*request_, SetProperty(request_resource_,
+ PP_URLREQUESTPROPERTY_HEADERS,
+ IsEqualToVar(var_headers))).Times(1);
+}
+
+void MountHttpNodeTest::SetResponse(int status_code, const char* headers) {
+ ON_CALL(*response_, GetProperty(response_resource_, _))
+ .WillByDefault(Return(PP_MakeUndefined()));
+
+ PP_Var var_headers = MakeString(348);
+ EXPECT_CALL(*response_,
+ GetProperty(response_resource_,
+ PP_URLRESPONSEPROPERTY_STATUSCODE))
+ .WillOnce(Return(PP_MakeInt32(status_code)));
+ EXPECT_CALL(*response_,
+ GetProperty(response_resource_, PP_URLRESPONSEPROPERTY_HEADERS))
+ .WillOnce(Return(var_headers));
+ EXPECT_CALL(*var_, VarToUtf8(IsEqualToVar(var_headers), _))
+ .WillOnce(DoAll(SetArgPointee<1>(strlen(headers)),
+ Return(headers)));
+}
+
+ACTION_P3(ReadResponseBodyAction, offset, body, body_length) {
+ char* buf = static_cast<char*>(arg1);
+ size_t read_length = arg2;
+ PP_CompletionCallback callback = arg3;
+ if (*offset >= body_length)
+ return 0;
+
+ read_length = std::min(read_length, body_length - *offset);
+ memcpy(buf, body + *offset, read_length);
+ *offset += read_length;
+
+ // Also call the callback.
+ if (callback.func)
+ (*callback.func)(callback.user_data, PP_OK);
+
+ return read_length;
+}
+
+void MountHttpNodeTest::SetResponseBody(const char* body) {
+ response_body_offset_ = 0;
+ EXPECT_CALL(*loader_, ReadResponseBody(loader_resource_, _, _, _))
+ .WillRepeatedly(ReadResponseBodyAction(
+ &response_body_offset_, body, strlen(body)));
+}
+
+void MountHttpNodeTest::OpenNode() {
+ node_ = mnt_->Open(Path(path_), O_RDONLY);
+ ASSERT_NE((MountNode*)NULL, node_);
+}
+
+void MountHttpNodeTest::ResetMocks() {
+ Mock::VerifyAndClearExpectations(&ppapi_);
+ Mock::VerifyAndClearExpectations(loader_);
+ Mock::VerifyAndClearExpectations(request_);
+ Mock::VerifyAndClearExpectations(response_);
+ Mock::VerifyAndClearExpectations(var_);
+}
+
+void MountHttpNodeTest::TearDown() {
+ if (node_)
+ mnt_->ReleaseNode(node_);
+ delete mnt_;
+}
+
+TEST_F(MountHttpNodeTest, OpenAndClose) {
+ SetMountArgs(StringMap_t());
+ ExpectOpen("HEAD");
+ ExpectHeaders("");
+ SetResponse(200, "");
+ OpenNode();
+}
+
+TEST_F(MountHttpNodeTest, ReadCached) {
+ SetMountArgs(StringMap_t());
+ ExpectOpen("HEAD");
+ ExpectHeaders("");
+ SetResponse(200, "Content-Length: 42\n");
+ OpenNode();
+ ResetMocks();
+
+ EXPECT_EQ(42, node_->GetSize());
+
+ char buf[10];
+ memset(&buf[0], 0, sizeof(buf));
+
+ ExpectOpen("GET");
+ ExpectHeaders("");
+ SetResponse(200, "Content-Length: 42\n");
+ SetResponseBody("Here is some response text. And some more.");
+ node_->Read(0, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("Here is s", &buf[0]);
+ ResetMocks();
+
+ // Further reads should be cached.
+ node_->Read(0, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("Here is s", &buf[0]);
+ node_->Read(10, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("me respon", &buf[0]);
+
+ EXPECT_EQ(42, node_->GetSize());
+}
+
+TEST_F(MountHttpNodeTest, ReadCachedNoContentLength) {
+ SetMountArgs(StringMap_t());
+ ExpectOpen("HEAD");
+ ExpectHeaders("");
+ SetResponse(200, "");
+ OpenNode();
+ ResetMocks();
+
+ // Unknown size.
+ EXPECT_EQ(0, node_->GetSize());
+
+ char buf[10];
+ memset(&buf[0], 0, sizeof(buf));
+
+ ExpectOpen("GET");
+ ExpectHeaders("");
+ SetResponse(200, ""); // No Content-Length response here.
+ SetResponseBody("Here is some response text. And some more.");
+ node_->Read(0, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("Here is s", &buf[0]);
+ ResetMocks();
+
+ // Further reads should be cached.
+ node_->Read(0, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("Here is s", &buf[0]);
+ node_->Read(10, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("me respon", &buf[0]);
+
+ EXPECT_EQ(42, node_->GetSize());
+}
+
+TEST_F(MountHttpNodeTest, ReadCachedUnderrun) {
+ SetMountArgs(StringMap_t());
+ ExpectOpen("HEAD");
+ ExpectHeaders("");
+ SetResponse(200, "Content-Length: 100\n");
+ OpenNode();
+ ResetMocks();
+
+ EXPECT_EQ(100, node_->GetSize());
+
+ char buf[10];
+ memset(&buf[0], 0, sizeof(buf));
+
+ ExpectOpen("GET");
+ ExpectHeaders("");
+ SetResponse(200, "Content-Length: 100\n");
+ SetResponseBody("abcdefghijklmnopqrstuvwxyz");
+ node_->Read(0, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("abcdefghi", &buf[0]);
+ ResetMocks();
+
+ EXPECT_EQ(26, node_->GetSize());
+}
+
+TEST_F(MountHttpNodeTest, ReadCachedOverrun) {
+ SetMountArgs(StringMap_t());
+ ExpectOpen("HEAD");
+ ExpectHeaders("");
+ SetResponse(200, "Content-Length: 15\n");
+ OpenNode();
+ ResetMocks();
+
+ EXPECT_EQ(15, node_->GetSize());
+
+ char buf[10];
+ memset(&buf[0], 0, sizeof(buf));
+
+ ExpectOpen("GET");
+ ExpectHeaders("");
+ SetResponse(200, "Content-Length: 15\n");
+ SetResponseBody("01234567890123456789");
+ node_->Read(10, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("01234", &buf[0]);
+ ResetMocks();
+
+ EXPECT_EQ(15, node_->GetSize());
+}
+
+TEST_F(MountHttpNodeTest, ReadPartial) {
+ StringMap_t args;
+ args["cache_content"] = "false";
+ SetMountArgs(args);
+ ExpectOpen("HEAD");
+ ExpectHeaders("");
+ SetResponse(200, "");
+ OpenNode();
+ ResetMocks();
+
+ char buf[10];
+ memset(&buf[0], 0, sizeof(buf));
+
+ ExpectOpen("GET");
+ ExpectHeaders("Range: bytes=0-8\n");
+ SetResponse(206, "Content-Length: 9\nContent-Range: bytes=0-8\n");
+ SetResponseBody("012345678");
+ node_->Read(0, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("012345678", &buf[0]);
+ ResetMocks();
+
+ // Another read is another request.
+ ExpectOpen("GET");
+ ExpectHeaders("Range: bytes=10-18\n");
+ SetResponse(206, "Content-Length: 9\nContent-Range: bytes=10-18\n");
+ SetResponseBody("abcdefghi");
+ node_->Read(10, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("abcdefghi", &buf[0]);
+}
+
+TEST_F(MountHttpNodeTest, ReadPartialNoServerSupport) {
+ StringMap_t args;
+ args["cache_content"] = "false";
+ SetMountArgs(args);
+ ExpectOpen("HEAD");
+ ExpectHeaders("");
+ SetResponse(200, "");
+ OpenNode();
+ ResetMocks();
+
+ char buf[10];
+ memset(&buf[0], 0, sizeof(buf));
+
+ ExpectOpen("GET");
+ ExpectHeaders("Range: bytes=10-18\n");
+ SetResponse(200, "Content-Length: 20\n");
+ SetResponseBody("0123456789abcdefghij");
+ node_->Read(10, buf, sizeof(buf) - 1);
+ EXPECT_STREQ("abcdefghi", &buf[0]);
+}