1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "nacl_io/httpfs/http_fs.h"
14 #include <sys/types.h>
18 #include <ppapi/c/pp_errors.h>
20 #include "nacl_io/dir_node.h"
21 #include "nacl_io/httpfs/http_fs_node.h"
22 #include "nacl_io/kernel_handle.h"
23 #include "nacl_io/log.h"
24 #include "nacl_io/osinttypes.h"
25 #include "nacl_io/osunistd.h"
26 #include "sdk_util/string_util.h"
30 std::string NormalizeHeaderKey(const std::string& s) {
31 // Capitalize the first letter and any letter following a hyphen:
32 // e.g. ACCEPT-ENCODING -> Accept-Encoding
35 for (size_t i = 0; i < s.length(); ++i) {
37 result += upper ? toupper(c) : tolower(c);
44 Error HttpFs::Access(const Path& path, int a_mode) {
45 NodeMap_t::iterator iter = node_cache_.find(path.Join());
46 if (iter == node_cache_.end()) {
47 // If we can't find the node in the cache, fetch it
48 std::string url = MakeUrl(path);
49 ScopedNode node(new HttpFsNode(this, url, cache_content_));
50 Error error = node->Init(0);
54 error = node->GetStat(NULL);
59 // Don't allow write or execute access.
60 if (a_mode & (W_OK | X_OK))
66 Error HttpFs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
67 out_node->reset(NULL);
69 NodeMap_t::iterator iter = node_cache_.find(path.Join());
70 if (iter != node_cache_.end()) {
71 *out_node = iter->second;
75 // If we can't find the node in the cache, create it
76 std::string url = MakeUrl(path);
77 ScopedNode node(new HttpFsNode(this, url, cache_content_));
78 Error error = node->Init(open_flags);
82 error = node->GetStat(NULL);
87 error = FindOrCreateDir(path.Parent(), &parent);
91 error = parent->AddChild(path.Basename(), node);
95 node_cache_[path.Join()] = node;
100 Error HttpFs::Unlink(const Path& path) {
101 NodeMap_t::iterator iter = node_cache_.find(path.Join());
102 if (iter == node_cache_.end())
105 if (iter->second->IsaDir())
111 Error HttpFs::Mkdir(const Path& path, int permissions) {
112 NodeMap_t::iterator iter = node_cache_.find(path.Join());
113 if (iter != node_cache_.end()) {
114 if (iter->second->IsaDir())
120 Error HttpFs::Rmdir(const Path& path) {
121 NodeMap_t::iterator iter = node_cache_.find(path.Join());
122 if (iter == node_cache_.end())
125 if (!iter->second->IsaDir())
131 Error HttpFs::Remove(const Path& path) {
132 NodeMap_t::iterator iter = node_cache_.find(path.Join());
133 if (iter == node_cache_.end())
139 Error HttpFs::Rename(const Path& path, const Path& newpath) {
140 NodeMap_t::iterator iter = node_cache_.find(path.Join());
141 if (iter == node_cache_.end())
147 PP_Resource HttpFs::MakeUrlRequestInfo(const std::string& url,
149 StringMap_t* additional_headers) {
150 URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface();
151 VarInterface* var_interface = ppapi_->GetVarInterface();
153 PP_Resource request_info = interface->Create(ppapi_->GetInstance());
157 interface->SetProperty(request_info,
158 PP_URLREQUESTPROPERTY_URL,
159 var_interface->VarFromUtf8(url.c_str(), url.length()));
160 interface->SetProperty(request_info,
161 PP_URLREQUESTPROPERTY_METHOD,
162 var_interface->VarFromUtf8(method, strlen(method)));
163 interface->SetProperty(request_info,
164 PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
165 PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE));
166 interface->SetProperty(request_info,
167 PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
168 PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE));
170 // Merge the filesystem headers with the request headers. If the field is
171 // already set it |additional_headers|, don't use the one from headers_.
172 for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end();
174 const std::string& key = NormalizeHeaderKey(iter->first);
175 if (additional_headers->find(key) == additional_headers->end()) {
176 additional_headers->insert(std::make_pair(key, iter->second));
180 // Join the headers into one string.
182 for (StringMap_t::iterator iter = additional_headers->begin();
183 iter != additional_headers->end();
185 headers += iter->first + ": " + iter->second + '\n';
188 interface->SetProperty(
190 PP_URLREQUESTPROPERTY_HEADERS,
191 var_interface->VarFromUtf8(headers.c_str(), headers.length()));
197 : allow_cors_(false),
198 allow_credentials_(false),
200 cache_content_(true) {
203 Error HttpFs::Init(const FsInitArgs& args) {
204 Error error = Filesystem::Init(args);
208 // Parse filesystem args.
209 for (StringMap_t::const_iterator iter = args.string_map.begin();
210 iter != args.string_map.end();
212 if (iter->first == "SOURCE") {
213 url_root_ = iter->second;
214 is_blob_url_ = strncmp(url_root_.c_str(), "blob:", 5) == 0;
216 // Make sure url_root_ ends with a slash, except for blob URLs.
217 if (!is_blob_url_ && !url_root_.empty() &&
218 url_root_[url_root_.length() - 1] != '/') {
221 } else if (iter->first == "manifest") {
223 error = LoadManifest(iter->second, &text);
227 error = ParseManifest(text);
234 } else if (iter->first == "allow_cross_origin_requests") {
235 allow_cors_ = iter->second == "true";
236 } else if (iter->first == "allow_credentials") {
237 allow_credentials_ = iter->second == "true";
238 } else if (iter->first == "cache_stat") {
239 cache_stat_ = iter->second == "true";
240 } else if (iter->first == "cache_content") {
241 cache_content_ = iter->second == "true";
243 // Assume it is a header to pass to an HTTP request.
244 headers_[NormalizeHeaderKey(iter->first)] = iter->second;
251 void HttpFs::Destroy() {
254 Error HttpFs::FindOrCreateDir(const Path& path, ScopedNode* out_node) {
255 out_node->reset(NULL);
256 std::string strpath = path.Join();
257 NodeMap_t::iterator iter = node_cache_.find(strpath);
258 if (iter != node_cache_.end()) {
259 *out_node = iter->second;
263 // If the node does not exist, create it.
264 ScopedNode node(new DirNode(this));
265 Error error = node->Init(0);
269 // If not the root node, find the parent node and add it to the parent
270 if (!path.IsRoot()) {
272 error = FindOrCreateDir(path.Parent(), &parent);
276 error = parent->AddChild(path.Basename(), node);
281 // Add it to the node cache.
282 node_cache_[strpath] = node;
287 Error HttpFs::ParseManifest(const char* text) {
288 std::vector<std::string> lines;
289 sdk_util::SplitString(text, '\n', &lines);
291 for (size_t i = 0; i < lines.size(); i++) {
292 std::vector<std::string> words;
293 sdk_util::SplitString(lines[i], ' ', &words);
295 // Remove empty words (due to multiple consecutive spaces).
296 std::vector<std::string> non_empty_words;
297 for (std::vector<std::string>::const_iterator it = words.begin();
301 non_empty_words.push_back(*it);
304 if (non_empty_words.size() == 3) {
305 const std::string& modestr = non_empty_words[0];
306 const std::string& lenstr = non_empty_words[1];
307 const std::string& name = non_empty_words[2];
309 assert(modestr.size() == 4);
310 assert(name[0] == '/');
312 // Only support regular and streams for now
315 switch (modestr[0]) {
323 LOG_ERROR("Unable to parse type %s for %s.",
329 switch (modestr[1]) {
333 mode |= S_IRUSR | S_IRGRP | S_IROTH;
336 LOG_ERROR("Unable to parse read %s for %s.",
342 switch (modestr[2]) {
346 mode |= S_IWUSR | S_IWGRP | S_IWOTH;
349 LOG_ERROR("Unable to parse write %s for %s.",
356 std::string url = MakeUrl(path);
358 HttpFsNode* http_node = new HttpFsNode(this, url, cache_content_);
359 http_node->SetMode(mode);
360 ScopedNode node(http_node);
362 Error error = node->Init(0);
365 http_node->SetCachedSize(atoi(lenstr.c_str()));
368 error = FindOrCreateDir(path.Parent(), &dir_node);
372 error = dir_node->AddChild(path.Basename(), node);
376 std::string pname = path.Join();
377 node_cache_[pname] = node;
384 Error HttpFs::LoadManifest(const std::string& manifest_name,
385 char** out_manifest) {
386 Path manifest_path(manifest_name);
387 ScopedNode manifest_node;
388 *out_manifest = NULL;
390 int error = Open(manifest_path, O_RDONLY, &manifest_node);
395 error = manifest_node->GetSize(&size);
399 char* text = (char*)malloc(size + 1);
400 assert(text != NULL);
404 error = manifest_node->Read(HandleAttr(), text, size, &len);
409 *out_manifest = text;
413 std::string HttpFs::MakeUrl(const Path& path) {
415 (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join());
418 } // namespace nacl_io