summaryrefslogtreecommitdiffstats
path: root/native_client_sdk/src
diff options
context:
space:
mode:
authorbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-22 18:52:15 +0000
committerbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-22 18:52:15 +0000
commitcd7541a7fbe2c0f343ca307ff6d0e3ac98885daa (patch)
tree7c4cc5f8485a093a938a27e321028ecb501420ed /native_client_sdk/src
parenta93e6564a2ceb51148422e158edfbd7e91891b62 (diff)
downloadchromium_src-cd7541a7fbe2c0f343ca307ff6d0e3ac98885daa.zip
chromium_src-cd7541a7fbe2c0f343ca307ff6d0e3ac98885daa.tar.gz
chromium_src-cd7541a7fbe2c0f343ca307ff6d0e3ac98885daa.tar.bz2
[NaCl SDK] nacl_io: Fix some httpfs bugs.
* Fix GetStat returning size=0, when there is no "Content-Length" header, and cache_stat is off. * Fix infinite loop when reading uncached past the end of the entity. * Fix error when doing partial read past the end of the entity. * Fix Fake URLLoader to handle partial content correctly. * Add parameterized HTTP tests (on each caching type, cache_stat/cache_content). BUG=320557 R=sbc@chromium.org Review URL: https://codereview.chromium.org/81483002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236799 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk/src')
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_node_http.cc361
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_node_http.h31
-rw-r--r--native_client_sdk/src/libraries/nacl_io/pepper_interface.cc14
-rw-r--r--native_client_sdk/src/libraries/nacl_io/pepper_interface.h9
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.cc155
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc214
6 files changed, 453 insertions, 331 deletions
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc
index 25135e4..66b63c7 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc
@@ -26,11 +26,12 @@ 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.
-const size_t MAX_READ_BUFFER_SIZE = 64 * 1024;
+const int MAX_READ_BUFFER_SIZE = 64 * 1024;
const int32_t STATUSCODE_OK = 200;
const int32_t STATUSCODE_PARTIAL_CONTENT = 206;
const int32_t STATUSCODE_FORBIDDEN = 403;
const int32_t STATUSCODE_NOT_FOUND = 404;
+const int32_t STATUSCODE_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
StringMap_t ParseHeaders(const char* headers, int32_t headers_length) {
enum State {
@@ -163,55 +164,7 @@ Error MountNodeHttp::GetDents(size_t offs,
Error MountNodeHttp::GetStat(struct stat* stat) {
AUTO_LOCK(node_lock_);
-
- // 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->cache_stat_) {
- StringMap_t headers;
- PP_Resource loader;
- PP_Resource request;
- PP_Resource response;
- int32_t statuscode;
- StringMap_t response_headers;
- Error error = OpenUrl("HEAD",
- &headers,
- &loader,
- &request,
- &response,
- &statuscode,
- &response_headers);
- if (error)
- return error;
-
- ScopedResource scoped_loader(mount_->ppapi(), loader);
- ScopedResource scoped_request(mount_->ppapi(), request);
- ScopedResource scoped_response(mount_->ppapi(), response);
-
- size_t entity_length;
- if (ParseContentLength(response_headers, &entity_length)) {
- SetCachedSize(static_cast<off_t>(entity_length));
- } else if (cache_content_ && !has_cached_size_) {
- error = DownloadToCache();
- if (error)
- return error;
- } else {
- // Don't use SetCachedSize here -- it is actually unknown.
- stat_.st_size = 0;
- }
-
- stat_.st_atime = 0; // TODO(binji): Use "Last-Modified".
- stat_.st_mtime = 0;
- stat_.st_ctime = 0;
-
- stat_.st_mode |= S_IFREG;
- }
-
- // Fill the stat structure if provided
- if (stat)
- *stat = stat_;
-
- return 0;
+ return GetStat_Locked(stat);
}
Error MountNodeHttp::Read(const HandleAttr& attr,
@@ -251,15 +204,10 @@ Error MountNodeHttp::GetSize(size_t* out_size) {
// TODO(binji): This value should be cached properly; i.e. obey the caching
// headers returned by the server.
AUTO_LOCK(node_lock_);
- if (!has_cached_size_) {
- // Even if DownloadToCache fails, the best result we can return is what
- // was written to stat_.st_size.
- if (cache_content_) {
- Error error = DownloadToCache();
- if (error)
- return error;
- }
- }
+ struct stat statbuf;
+ Error error = GetStat_Locked(&statbuf);
+ if (error)
+ return error;
*out_size = stat_.st_size;
return 0;
@@ -273,24 +221,83 @@ MountNodeHttp::MountNodeHttp(Mount* mount,
cache_content_(cache_content),
has_cached_size_(false) {}
-void MountNodeHttp::SetMode(int mode) {
- stat_.st_mode = mode;
+void MountNodeHttp::SetMode(int mode) { stat_.st_mode = mode; }
+
+Error MountNodeHttp::GetStat_Locked(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 (!has_cached_size_ || !mount->cache_stat_) {
+ StringMap_t headers;
+ ScopedResource loader(mount_->ppapi());
+ ScopedResource request(mount_->ppapi());
+ ScopedResource response(mount_->ppapi());
+ int32_t statuscode;
+ StringMap_t response_headers;
+ Error error = OpenUrl("HEAD",
+ &headers,
+ &loader,
+ &request,
+ &response,
+ &statuscode,
+ &response_headers);
+ if (error)
+ return error;
+
+ size_t entity_length;
+ if (ParseContentLength(response_headers, &entity_length)) {
+ SetCachedSize(static_cast<off_t>(entity_length));
+ } else if (cache_content_) {
+ // The server didn't give a content length; download the data to memory
+ // via DownloadToCache, which will also set stat_.st_size;
+ error = DownloadToCache();
+ if (error)
+ return error;
+ } else {
+ // The user doesn't want to cache content, but we didn't get a
+ // "Content-Length" header. Read the entire entity, and throw it away.
+ // Don't use DownloadToCache, as that will still allocate enough memory
+ // for the entire entity.
+ int bytes_read;
+ error = DownloadToTemp(&bytes_read);
+ if (error)
+ return error;
+
+ SetCachedSize(bytes_read);
+ }
+
+ stat_.st_atime = 0; // TODO(binji): Use "Last-Modified".
+ stat_.st_mtime = 0;
+ stat_.st_ctime = 0;
+
+ stat_.st_mode |= S_IFREG;
+ }
+
+ // Fill the stat structure if provided
+ if (stat)
+ *stat = stat_;
+
+ return 0;
}
Error MountNodeHttp::OpenUrl(const char* method,
StringMap_t* request_headers,
- PP_Resource* out_loader,
- PP_Resource* out_request,
- PP_Resource* out_response,
+ ScopedResource* out_loader,
+ ScopedResource* out_request,
+ ScopedResource* out_response,
int32_t* out_statuscode,
StringMap_t* out_response_headers) {
+ // Clear all out parameters.
+ *out_statuscode = 0;
+ out_response_headers->clear();
+
// 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())
+ out_request->Reset(
+ mount_http->MakeUrlRequestInfo(url_, method, request_headers));
+ if (!out_request->pp_resource())
return EINVAL;
URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface();
@@ -298,23 +305,24 @@ Error MountNodeHttp::OpenUrl(const char* method,
ppapi->GetURLResponseInfoInterface();
VarInterface* var_interface = ppapi->GetVarInterface();
- ScopedResource loader(ppapi, loader_interface->Create(ppapi->GetInstance()));
- if (!loader.pp_resource())
+ out_loader->Reset(loader_interface->Create(ppapi->GetInstance()));
+ if (!out_loader->pp_resource())
return EINVAL;
- int32_t result = loader_interface->Open(
- loader.pp_resource(), request.pp_resource(), PP_BlockUntilComplete());
+ int32_t result = loader_interface->Open(out_loader->pp_resource(),
+ out_request->pp_resource(),
+ PP_BlockUntilComplete());
if (result != PP_OK)
return PPErrorToErrno(result);
- ScopedResource response(
- ppapi, loader_interface->GetResponseInfo(loader.pp_resource()));
- if (!response.pp_resource())
+ out_response->Reset(
+ loader_interface->GetResponseInfo(out_loader->pp_resource()));
+ if (!out_response->pp_resource())
return EINVAL;
// Get response statuscode.
PP_Var statuscode = response_interface->GetProperty(
- response.pp_resource(), PP_URLRESPONSEPROPERTY_STATUSCODE);
+ out_response->pp_resource(), PP_URLRESPONSEPROPERTY_STATUSCODE);
if (statuscode.type != PP_VARTYPE_INT32)
return EINVAL;
@@ -328,15 +336,12 @@ Error MountNodeHttp::OpenUrl(const char* method,
// Get response headers.
PP_Var response_headers_var = response_interface->GetProperty(
- response.pp_resource(), PP_URLRESPONSEPROPERTY_HEADERS);
+ out_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);
@@ -347,9 +352,9 @@ Error MountNodeHttp::OpenUrl(const char* method,
Error MountNodeHttp::DownloadToCache() {
StringMap_t headers;
- PP_Resource loader;
- PP_Resource request;
- PP_Resource response;
+ ScopedResource loader(mount_->ppapi());
+ ScopedResource request(mount_->ppapi());
+ ScopedResource response(mount_->ppapi());
int32_t statuscode;
StringMap_t response_headers;
Error error = OpenUrl("GET",
@@ -362,16 +367,11 @@ Error MountNodeHttp::DownloadToCache() {
if (error)
return error;
- 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;
- error = DownloadToBuffer(
+ error = ReadResponseToBuffer(
loader, cached_data_.data(), content_length, &real_size);
if (error)
return error;
@@ -381,27 +381,13 @@ Error MountNodeHttp::DownloadToCache() {
return 0;
}
- // We don't know how big the file is. Read in chunks.
- cached_data_.resize(MAX_READ_BUFFER_SIZE);
- int total_bytes_read = 0;
- int bytes_to_read = MAX_READ_BUFFER_SIZE;
- while (true) {
- char* buf = cached_data_.data() + total_bytes_read;
- int bytes_read;
- error = DownloadToBuffer(loader, buf, bytes_to_read, &bytes_read);
- if (error)
- return error;
-
- total_bytes_read += bytes_read;
-
- if (bytes_read < bytes_to_read) {
- SetCachedSize(total_bytes_read);
- cached_data_.resize(total_bytes_read);
- return 0;
- }
+ int bytes_read;
+ error = ReadEntireResponseToCache(loader, &bytes_read);
+ if (error)
+ return error;
- cached_data_.resize(total_bytes_read + bytes_to_read);
- }
+ SetCachedSize(bytes_read);
+ return 0;
}
Error MountNodeHttp::ReadPartialFromCache(const HandleAttr& attr,
@@ -439,9 +425,9 @@ Error MountNodeHttp::DownloadPartial(const HandleAttr& attr,
attr.offs + count - 1);
headers["Range"] = buffer;
- PP_Resource loader;
- PP_Resource request;
- PP_Resource response;
+ ScopedResource loader(mount_->ppapi());
+ ScopedResource request(mount_->ppapi());
+ ScopedResource response(mount_->ppapi());
int32_t statuscode;
StringMap_t response_headers;
Error error = OpenUrl("GET",
@@ -451,13 +437,15 @@ Error MountNodeHttp::DownloadPartial(const HandleAttr& attr,
&response,
&statuscode,
&response_headers);
- if (error)
- return error;
+ if (error) {
+ if (statuscode == STATUSCODE_REQUESTED_RANGE_NOT_SATISFIABLE) {
+ // We're likely trying to read past the end. Return 0 bytes.
+ *out_bytes = 0;
+ return 0;
+ }
- PepperInterface* ppapi = mount_->ppapi();
- ScopedResource scoped_loader(ppapi, loader);
- ScopedResource scoped_request(ppapi, request);
- ScopedResource scoped_response(ppapi, response);
+ return error;
+ }
size_t read_start = 0;
if (statuscode == STATUSCODE_OK) {
@@ -497,28 +485,127 @@ Error MountNodeHttp::DownloadPartial(const HandleAttr& attr,
if (read_start < attr.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 = attr.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;
- Error error =
- DownloadToBuffer(loader, buffer_.data(), buffer_.size(), &bytes_read);
- if (error)
- return error;
+ int bytes_to_read = attr.offs - read_start;
+ int bytes_read;
+ error = ReadResponseToTemp(loader, bytes_to_read, &bytes_read);
+ if (error)
+ return error;
- bytes_to_read -= bytes_read;
+ // Tried to read past the end of the entity.
+ if (bytes_read < bytes_to_read) {
+ *out_bytes = 0;
+ return 0;
}
}
- return DownloadToBuffer(loader, buf, count, out_bytes);
+ return ReadResponseToBuffer(loader, buf, count, out_bytes);
+}
+
+Error MountNodeHttp::DownloadToTemp(int* out_bytes) {
+ StringMap_t headers;
+ ScopedResource loader(mount_->ppapi());
+ ScopedResource request(mount_->ppapi());
+ ScopedResource response(mount_->ppapi());
+ int32_t statuscode;
+ StringMap_t response_headers;
+ Error error = OpenUrl("GET",
+ &headers,
+ &loader,
+ &request,
+ &response,
+ &statuscode,
+ &response_headers);
+ if (error)
+ return error;
+
+ size_t content_length = 0;
+ if (ParseContentLength(response_headers, &content_length)) {
+ *out_bytes = content_length;
+ return 0;
+ }
+
+ return ReadEntireResponseToTemp(loader, out_bytes);
}
-Error MountNodeHttp::DownloadToBuffer(PP_Resource loader,
- void* buf,
- int count,
- int* out_bytes) {
+Error MountNodeHttp::ReadEntireResponseToTemp(const ScopedResource& loader,
+ int* out_bytes) {
+ *out_bytes = 0;
+
+ const int kBytesToRead = MAX_READ_BUFFER_SIZE;
+ buffer_.resize(kBytesToRead);
+
+ while (true) {
+ int bytes_read;
+ Error error =
+ ReadResponseToBuffer(loader, buffer_.data(), kBytesToRead, &bytes_read);
+ if (error)
+ return error;
+
+ *out_bytes += bytes_read;
+
+ if (bytes_read < kBytesToRead)
+ return 0;
+ }
+}
+
+Error MountNodeHttp::ReadEntireResponseToCache(const ScopedResource& loader,
+ int* out_bytes) {
+ *out_bytes = 0;
+ const int kBytesToRead = MAX_READ_BUFFER_SIZE;
+
+ while (true) {
+ // Always recalculate the buf pointer because it may have moved when
+ // cached_data_ was resized.
+ cached_data_.resize(*out_bytes + kBytesToRead);
+ void* buf = cached_data_.data() + *out_bytes;
+
+ int bytes_read;
+ Error error = ReadResponseToBuffer(loader, buf, kBytesToRead, &bytes_read);
+ if (error)
+ return error;
+
+ *out_bytes += bytes_read;
+
+ if (bytes_read < kBytesToRead) {
+ // Shrink the cached data buffer to the correct size.
+ cached_data_.resize(*out_bytes);
+ return 0;
+ }
+ }
+}
+
+Error MountNodeHttp::ReadResponseToTemp(const ScopedResource& loader,
+ int count,
+ int* out_bytes) {
+ *out_bytes = 0;
+
+ if (buffer_.size() < static_cast<size_t>(count))
+ buffer_.resize(std::min(count, MAX_READ_BUFFER_SIZE));
+
+ int bytes_left = count;
+ while (bytes_left > 0) {
+ int bytes_to_read =
+ std::min(static_cast<size_t>(bytes_left), buffer_.size());
+ int bytes_read;
+ Error error = ReadResponseToBuffer(
+ loader, buffer_.data(), bytes_to_read, &bytes_read);
+ if (error)
+ return error;
+
+ if (bytes_read == 0)
+ return 0;
+
+ bytes_left -= bytes_read;
+ *out_bytes += bytes_read;
+ }
+
+ return 0;
+}
+
+Error MountNodeHttp::ReadResponseToBuffer(const ScopedResource& loader,
+ void* buf,
+ int count,
+ int* out_bytes) {
*out_bytes = 0;
PepperInterface* ppapi = mount_->ppapi();
@@ -527,8 +614,11 @@ Error MountNodeHttp::DownloadToBuffer(PP_Resource loader,
char* out_buffer = static_cast<char*>(buf);
int bytes_to_read = count;
while (bytes_to_read > 0) {
- int bytes_read = loader_interface->ReadResponseBody(
- loader, out_buffer, bytes_to_read, PP_BlockUntilComplete());
+ int bytes_read =
+ loader_interface->ReadResponseBody(loader.pp_resource(),
+ 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
@@ -550,4 +640,3 @@ Error MountNodeHttp::DownloadToBuffer(PP_Resource loader,
}
} // namespace nacl_io
-
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.h b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h
index 9ea5f14..9ad2ab2 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_node_http.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h
@@ -43,11 +43,12 @@ class MountNodeHttp : public MountNode {
MountNodeHttp(Mount* mount, const std::string& url, bool cache_content);
private:
+ Error GetStat_Locked(struct stat* stat);
Error OpenUrl(const char* method,
StringMap_t* request_headers,
- PP_Resource* out_loader,
- PP_Resource* out_request,
- PP_Resource* out_response,
+ ScopedResource* out_loader,
+ ScopedResource* out_request,
+ ScopedResource* out_response,
int32_t* out_statuscode,
StringMap_t* out_response_headers);
@@ -60,10 +61,26 @@ class MountNodeHttp : public MountNode {
void* buf,
size_t count,
int* out_bytes);
- Error DownloadToBuffer(PP_Resource loader,
- void* buf,
- int count,
- int* out_bytes);
+
+ Error DownloadToTemp(int* out_bytes);
+
+ // Read as much as possible from |loader|, using |buffer_| as a scratch area.
+ Error ReadEntireResponseToTemp(const ScopedResource& loader, int* out_bytes);
+ // Read as much as possible from |loader|, storing the result in
+ // |cached_data_|.
+ Error ReadEntireResponseToCache(const ScopedResource& loader, int* out_bytes);
+
+ // Read up to |count| bytes from |loader|, using |buffer_| as a scratch area.
+ Error ReadResponseToTemp(const ScopedResource& loader,
+ int count,
+ int* out_bytes);
+
+ // Read up to |count| bytes from |loader|, and store the result in |buf|,
+ // which is assumed to have |count| bytes free.
+ Error ReadResponseToBuffer(const ScopedResource& loader,
+ void* buf,
+ int count,
+ int* out_bytes);
std::string url_;
std::vector<char> buffer_;
diff --git a/native_client_sdk/src/libraries/nacl_io/pepper_interface.cc b/native_client_sdk/src/libraries/nacl_io/pepper_interface.cc
index 84e8305..46591a5 100644
--- a/native_client_sdk/src/libraries/nacl_io/pepper_interface.cc
+++ b/native_client_sdk/src/libraries/nacl_io/pepper_interface.cc
@@ -16,16 +16,24 @@ void PepperInterface::ReleaseResource(PP_Resource resource) {
GetCoreInterface()->ReleaseResource(resource);
}
+ScopedResource::ScopedResource(PepperInterface* ppapi)
+ : ppapi_(ppapi), resource_(0) {}
+
ScopedResource::ScopedResource(PepperInterface* ppapi, PP_Resource resource)
- : ppapi_(ppapi),
- resource_(resource) {
-}
+ : ppapi_(ppapi), resource_(resource) {}
ScopedResource::~ScopedResource() {
if (resource_)
ppapi_->ReleaseResource(resource_);
}
+void ScopedResource::Reset(PP_Resource resource) {
+ if (resource_)
+ ppapi_->ReleaseResource(resource_);
+
+ resource_ = resource;
+}
+
PP_Resource ScopedResource::Release() {
PP_Resource result = resource_;
resource_ = 0;
diff --git a/native_client_sdk/src/libraries/nacl_io/pepper_interface.h b/native_client_sdk/src/libraries/nacl_io/pepper_interface.h
index ac8f6ba..e311229 100644
--- a/native_client_sdk/src/libraries/nacl_io/pepper_interface.h
+++ b/native_client_sdk/src/libraries/nacl_io/pepper_interface.h
@@ -130,11 +130,16 @@ class PepperInterface {
class ScopedResource {
public:
- // Does not AddRef by default.
+ // Does not AddRef.
+ explicit ScopedResource(PepperInterface* ppapi);
ScopedResource(PepperInterface* ppapi, PP_Resource resource);
~ScopedResource();
- PP_Resource pp_resource() { return resource_; }
+ PP_Resource pp_resource() const { return resource_; }
+
+ // Set a new resource, releasing the old one. Does not AddRef the new
+ // resource.
+ void Reset(PP_Resource resource);
// Return the resource without decrementing its refcount.
PP_Resource Release();
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
index 1b6b92c..f0dc472 100644
--- 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
@@ -116,6 +116,70 @@ int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) {
return result;
}
+void HandleContentLength(FakeURLLoaderResource* loader,
+ FakeURLResponseInfoResource* response,
+ FakeURLLoaderEntity* entity) {
+ size_t content_length = entity->body().size();
+ if (!loader->server->send_content_length())
+ return;
+
+ std::ostringstream ss;
+ ss << "Content-Length: " << content_length << "\n";
+ response->headers += ss.str();
+}
+
+void HandlePartial(FakeURLLoaderResource* loader,
+ FakeURLRequestInfoResource* request,
+ FakeURLResponseInfoResource* response,
+ FakeURLLoaderEntity* entity) {
+ if (!loader->server->allow_partial())
+ return;
+
+ // Read the RFC on byte ranges for more info:
+ // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
+ std::string range;
+ if (!GetHeaderValue(request->headers, "Range", &range))
+ return;
+
+ // We don't support all range requests, just bytes=<num>-<num>
+ unsigned lo;
+ unsigned hi;
+ if (sscanf(range.c_str(), "bytes=%u-%u", &lo, &hi) != 2) {
+ // Couldn't parse the range value.
+ return;
+ }
+
+ size_t content_length = entity->body().size();
+ if (lo > content_length) {
+ // Trying to start reading past the end of the entity is
+ // unsatisfiable.
+ response->status_code = 416; // Request range not satisfiable.
+ return;
+ }
+
+ // Clamp the hi value to the content length.
+ if (hi >= content_length)
+ hi = content_length - 1;
+
+ if (lo > hi) {
+ // Bad range, ignore it and return the full result.
+ return;
+ }
+
+ // 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->read_offset = lo;
+ loader->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
+}
+
} // namespace
FakeURLLoaderEntity::FakeURLLoaderEntity(const std::string& body)
@@ -193,95 +257,64 @@ PP_Resource FakeURLLoaderInterface::Create(PP_Instance instance) {
}
int32_t FakeURLLoaderInterface::Open(PP_Resource loader,
- PP_Resource request_info,
+ PP_Resource request,
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 =
+ FakeURLRequestInfoResource* request_resource =
core_interface_->resource_manager()->Get<FakeURLRequestInfoResource>(
- request_info);
- if (request_info_resource == NULL)
+ request);
+ if (request_resource == NULL)
return PP_ERROR_BADRESOURCE;
// Create a response resource.
- FakeURLResponseInfoResource* response = new FakeURLResponseInfoResource;
+ FakeURLResponseInfoResource* response_resource =
+ new FakeURLResponseInfoResource;
loader_resource->response =
CREATE_RESOURCE(core_interface_->resource_manager(),
FakeURLResponseInfoResource,
- response);
+ response_resource);
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;
+ std::string url = request_resource->url;
+ std::string method = request_resource->method;
- response->url = url;
+ response_resource->url = url;
// TODO(binji): allow this to be set?
- response->headers.clear();
+ response_resource->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.
- }
+ response_resource->status_code = http_status_code;
+ return RunCompletionCallback(&callback, PP_OK);
+ }
- 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.
- }
- }
- }
- }
+ // Look up the URL in the loader resource entity map.
+ FakeURLLoaderEntity* entity = loader_resource->server->GetEntity(url);
+ response_resource->status_code = entity ? 200 : 404;
+
+ if (method == "GET") {
+ loader_resource->entity = entity;
+ } else if (method != "HEAD") {
+ response_resource->status_code = 405; // Method not allowed.
+ return RunCompletionCallback(&callback, PP_OK);
+ }
+
+ if (entity != NULL) {
+ size_t content_length = entity->body().size();
+ loader_resource->read_end = content_length;
+ HandleContentLength(loader_resource, response_resource, entity);
+ HandlePartial(loader_resource, request_resource, response_resource, entity);
}
// Call the callback.
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc
index 79a2fcb..bc7655a 100644
--- a/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc
@@ -34,39 +34,46 @@ class MountHttpForTesting : public MountHttp {
using MountHttp::FindOrCreateDir;
};
-class MountHttpTest : public ::testing::Test {
- public:
- MountHttpTest();
-
- protected:
- FakePepperInterfaceURLLoader ppapi_;
- MountHttpForTesting mnt_;
+enum {
+ kStringMapParamCacheNone = 0,
+ kStringMapParamCacheContent = 1,
+ kStringMapParamCacheStat = 2,
+ kStringMapParamCacheContentStat =
+ kStringMapParamCacheContent | kStringMapParamCacheStat,
};
+typedef uint32_t StringMapParam;
-MountHttpTest::MountHttpTest() : mnt_(StringMap_t(), &ppapi_) {}
-
-StringMap_t StringMap_NoCache() {
+StringMap_t MakeStringMap(StringMapParam param) {
StringMap_t smap;
- smap["cache_content"] = "false";
+ if (param & kStringMapParamCacheContent)
+ smap["cache_content"] = "true";
+ else
+ smap["cache_content"] = "false";
+
+ if (param & kStringMapParamCacheStat)
+ smap["cache_stat"] = "true";
+ else
+ smap["cache_stat"] = "false";
return smap;
}
-class MountHttpNoCacheTest : public ::testing::Test {
+class MountHttpTest : public ::testing::TestWithParam<StringMapParam> {
public:
- MountHttpNoCacheTest();
+ MountHttpTest();
protected:
FakePepperInterfaceURLLoader ppapi_;
MountHttpForTesting mnt_;
};
-MountHttpNoCacheTest::MountHttpNoCacheTest() :
- mnt_(StringMap_NoCache(), &ppapi_) {}
+MountHttpTest::MountHttpTest() :
+ mnt_(MakeStringMap(GetParam()), &ppapi_) {
+}
} // namespace
-TEST_F(MountHttpTest, Access) {
+TEST_P(MountHttpTest, Access) {
ASSERT_TRUE(ppapi_.server_template()->AddEntity("foo", "", NULL));
ASSERT_EQ(0, mnt_.Access(Path("/foo"), R_OK));
@@ -75,34 +82,74 @@ TEST_F(MountHttpTest, Access) {
ASSERT_EQ(ENOENT, mnt_.Access(Path("/bar"), F_OK));
}
-TEST_F(MountHttpTest, Read) {
- const char contents[] = "contents";
+TEST_P(MountHttpTest, OpenAndCloseServerError) {
+ EXPECT_TRUE(ppapi_.server_template()->AddError("file", 500));
+
+ ScopedMountNode node;
+ ASSERT_EQ(ENOENT, mnt_.Open(Path("/file"), O_RDONLY, &node));
+}
+
+TEST_P(MountHttpTest, ReadPartial) {
+ const char contents[] = "0123456789abcdefg";
ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
+ ppapi_.server_template()->set_allow_partial(true);
+
+ int result_bytes = 0;
+
+ char buf[10];
+ memset(&buf[0], 0, sizeof(buf));
ScopedMountNode node;
ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node));
+ HandleAttr attr;
+ EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
+ EXPECT_EQ(sizeof(buf) - 1, result_bytes);
+ EXPECT_STREQ("012345678", &buf[0]);
+
+ // Read is clamped when reading past the end of the file.
+ attr.offs = 10;
+ ASSERT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
+ ASSERT_EQ(strlen("abcdefg"), result_bytes);
+ buf[result_bytes] = 0;
+ EXPECT_STREQ("abcdefg", &buf[0]);
+
+ // Read nothing when starting past the end of the file.
+ attr.offs = 100;
+ EXPECT_EQ(0, node->Read(attr, &buf[0], sizeof(buf), &result_bytes));
+ EXPECT_EQ(0, result_bytes);
+}
+
+TEST_P(MountHttpTest, ReadPartialNoServerSupport) {
+ const char contents[] = "0123456789abcdefg";
+ ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
+ ppapi_.server_template()->set_allow_partial(false);
- char buffer[10] = {0};
- int bytes_read = 0;
+ int result_bytes = 0;
+
+ char buf[10];
+ memset(&buf[0], 0, sizeof(buf));
+
+ ScopedMountNode node;
+ ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node));
HandleAttr attr;
- EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
- EXPECT_EQ(strlen(contents), bytes_read);
- EXPECT_STREQ(contents, buffer);
+ EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
+ EXPECT_EQ(sizeof(buf) - 1, result_bytes);
+ EXPECT_STREQ("012345678", &buf[0]);
- // Read nothing past the end of the file.
+ // Read is clamped when reading past the end of the file.
+ attr.offs = 10;
+ ASSERT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
+ ASSERT_EQ(strlen("abcdefg"), result_bytes);
+ buf[result_bytes] = 0;
+ EXPECT_STREQ("abcdefg", &buf[0]);
+
+ // Read nothing when starting past the end of the file.
attr.offs = 100;
- EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
- EXPECT_EQ(0, bytes_read);
-
- // Read part of the data.
- attr.offs = 4;
- EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
- ASSERT_EQ(strlen(contents) - 4, bytes_read);
- buffer[bytes_read] = 0;
- EXPECT_STREQ("ents", buffer);
+ EXPECT_EQ(0, node->Read(attr, &buf[0], sizeof(buf), &result_bytes));
+ EXPECT_EQ(0, result_bytes);
}
-TEST_F(MountHttpTest, Write) {
+TEST_P(MountHttpTest, Write) {
const char contents[] = "contents";
ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
@@ -117,7 +164,7 @@ TEST_F(MountHttpTest, Write) {
EXPECT_EQ(0, bytes_written);
}
-TEST_F(MountHttpTest, GetStat) {
+TEST_P(MountHttpTest, GetStat) {
const char contents[] = "contents";
ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
@@ -135,7 +182,7 @@ TEST_F(MountHttpTest, GetStat) {
EXPECT_EQ(0, statbuf.st_mtime);
}
-TEST_F(MountHttpTest, FTruncate) {
+TEST_P(MountHttpTest, FTruncate) {
const char contents[] = "contents";
ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
@@ -144,6 +191,15 @@ TEST_F(MountHttpTest, FTruncate) {
EXPECT_EQ(EACCES, node->FTruncate(4));
}
+// Instantiate the above tests for all caching types.
+INSTANTIATE_TEST_CASE_P(
+ Default,
+ MountHttpTest,
+ ::testing::Values((uint32_t)kStringMapParamCacheNone,
+ (uint32_t)kStringMapParamCacheContent,
+ (uint32_t)kStringMapParamCacheStat,
+ (uint32_t)kStringMapParamCacheContentStat));
+
TEST(MountHttpDirTest, Mkdir) {
StringMap_t args;
MountHttpForTesting mnt(args, NULL);
@@ -241,89 +297,3 @@ TEST(MountHttpDirTest, ParseManifest) {
EXPECT_EQ(234, sbar.st_size);
EXPECT_EQ(S_IFREG | S_IRALL | S_IWALL, sbar.st_mode);
}
-
-TEST_F(MountHttpNoCacheTest, OpenAndClose) {
- const char contents[] = "contents";
- ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
-
- ScopedMountNode node;
- ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node));
-}
-
-TEST_F(MountHttpNoCacheTest, OpenAndCloseNotFound) {
- ScopedMountNode node;
- ASSERT_EQ(ENOENT, mnt_.Open(Path("/file"), O_RDONLY, &node));
-}
-
-TEST_F(MountHttpNoCacheTest, OpenAndCloseServerError) {
- ASSERT_TRUE(ppapi_.server_template()->AddError("file", 500));
-
- ScopedMountNode node;
- ASSERT_EQ(ENOENT, mnt_.Open(Path("/file"), O_RDONLY, &node));
-}
-
-TEST_F(MountHttpNoCacheTest, GetSize) {
- const char contents[] = "contents";
- ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
- ppapi_.server_template()->set_send_content_length(true);
-
- ScopedMountNode node;
- struct stat statbuf;
- ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node));
- EXPECT_EQ(0, node->GetStat(&statbuf));
- EXPECT_EQ(strlen(contents), statbuf.st_size);
-}
-
-TEST_F(MountHttpNoCacheTest, Access) {
- ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", "", NULL));
-
- ASSERT_EQ(0, mnt_.Access(Path("/file"), R_OK));
- ASSERT_EQ(EACCES, mnt_.Access(Path("/file"), W_OK));
- ASSERT_EQ(ENOENT, mnt_.Access(Path("/bar"), R_OK));
-}
-
-TEST_F(MountHttpNoCacheTest, ReadPartial) {
- const char contents[] = "0123456789abcdefghi";
- ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
- ppapi_.server_template()->set_allow_partial(true);
-
- int result_bytes = 0;
-
- char buf[10];
- memset(&buf[0], 0, sizeof(buf));
-
- ScopedMountNode node;
- ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node));
- HandleAttr attr;
- EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
- EXPECT_EQ(sizeof(buf) - 1, result_bytes);
- EXPECT_STREQ("012345678", &buf[0]);
-
- attr.offs = 10;
- EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
- EXPECT_EQ(sizeof(buf) - 1, result_bytes);
- EXPECT_STREQ("abcdefghi", &buf[0]);
-}
-
-TEST_F(MountHttpNoCacheTest, ReadPartialNoServerSupport) {
- const char contents[] = "0123456789abcdefghi";
- ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
- ppapi_.server_template()->set_allow_partial(false);
-
- int result_bytes = 0;
-
- char buf[10];
- memset(&buf[0], 0, sizeof(buf));
-
- ScopedMountNode node;
- ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node));
- HandleAttr attr;
- EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
- EXPECT_EQ(sizeof(buf) - 1, result_bytes);
- EXPECT_STREQ("012345678", &buf[0]);
-
- attr.offs = 10;
- EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
- EXPECT_EQ(sizeof(buf) - 1, result_bytes);
- EXPECT_STREQ("abcdefghi", &buf[0]);
-}