Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / libraries / nacl_io / httpfs / http_fs.cc
blob3a76f8cfd6b6ae062f617c8315b35b8b9353eb3d
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"
7 #include <assert.h>
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
16 #include <vector>
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"
28 namespace nacl_io {
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
33 std::string result;
34 bool upper = true;
35 for (size_t i = 0; i < s.length(); ++i) {
36 char c = s[i];
37 result += upper ? toupper(c) : tolower(c);
38 upper = c == '-';
41 return result;
44 Error HttpFs::OpenWithMode(const Path& path, int open_flags, mode_t mode,
45 ScopedNode* out_node) {
46 out_node->reset(NULL);
48 ScopedNode node = FindExistingNode(path);
49 if (node.get() != NULL) {
50 *out_node = node;
51 return 0;
54 // If we can't find the node in the cache, create it
55 std::string url = MakeUrl(path);
56 node.reset(new HttpFsNode(this, url, cache_content_));
57 Error error = node->Init(open_flags);
58 if (error)
59 return error;
61 error = node->GetStat(NULL);
62 if (error)
63 return error;
65 ScopedNode parent;
66 error = FindOrCreateDir(path.Parent(), &parent);
67 if (error)
68 return error;
70 error = parent->AddChild(path.Basename(), node);
71 if (error)
72 return error;
74 node_cache_[path.Join()] = node;
75 *out_node = node;
76 return 0;
79 ScopedNode HttpFs::FindExistingNode(const Path& path) {
80 NodeMap_t::iterator iter = node_cache_.find(path.Join());
81 if (iter == node_cache_.end())
82 return ScopedNode();
83 return iter->second;
86 Error HttpFs::Unlink(const Path& path) {
87 ScopedNode node = FindExistingNode(path);
88 if (node.get() == NULL)
89 return ENOENT;
91 if (node->IsaDir())
92 return EISDIR;
94 return EACCES;
97 Error HttpFs::Mkdir(const Path& path, int permissions) {
98 ScopedNode node = FindExistingNode(path);
99 if (node.get() != NULL && node->IsaDir())
100 return EEXIST;
102 return EACCES;
105 Error HttpFs::Rmdir(const Path& path) {
106 ScopedNode node = FindExistingNode(path);
107 if (node.get() == NULL)
108 return ENOENT;
110 if (!node->IsaDir())
111 return ENOTDIR;
113 return EACCES;
116 Error HttpFs::Remove(const Path& path) {
117 ScopedNode node = FindExistingNode(path);
118 if (node.get() == NULL)
119 return ENOENT;
121 return EACCES;
124 Error HttpFs::Rename(const Path& path, const Path& newpath) {
125 ScopedNode node = FindExistingNode(path);
126 if (node.get() == NULL)
127 return ENOENT;
129 return EACCES;
132 PP_Resource HttpFs::MakeUrlRequestInfo(const std::string& url,
133 const char* method,
134 StringMap_t* additional_headers) {
135 URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface();
136 VarInterface* var_interface = ppapi_->GetVarInterface();
138 PP_Resource request_info = interface->Create(ppapi_->GetInstance());
139 if (!request_info)
140 return 0;
142 interface->SetProperty(request_info,
143 PP_URLREQUESTPROPERTY_URL,
144 var_interface->VarFromUtf8(url.c_str(), url.length()));
145 interface->SetProperty(request_info,
146 PP_URLREQUESTPROPERTY_METHOD,
147 var_interface->VarFromUtf8(method, strlen(method)));
148 interface->SetProperty(request_info,
149 PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
150 PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE));
151 interface->SetProperty(request_info,
152 PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
153 PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE));
155 // Merge the filesystem headers with the request headers. If the field is
156 // already set it |additional_headers|, don't use the one from headers_.
157 for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end();
158 ++iter) {
159 const std::string& key = NormalizeHeaderKey(iter->first);
160 if (additional_headers->find(key) == additional_headers->end()) {
161 additional_headers->insert(std::make_pair(key, iter->second));
165 // Join the headers into one string.
166 std::string headers;
167 for (StringMap_t::iterator iter = additional_headers->begin();
168 iter != additional_headers->end();
169 ++iter) {
170 headers += iter->first + ": " + iter->second + '\n';
173 interface->SetProperty(
174 request_info,
175 PP_URLREQUESTPROPERTY_HEADERS,
176 var_interface->VarFromUtf8(headers.c_str(), headers.length()));
178 return request_info;
181 HttpFs::HttpFs()
182 : allow_cors_(false),
183 allow_credentials_(false),
184 cache_stat_(true),
185 cache_content_(true),
186 is_blob_url_(false) {
189 Error HttpFs::Init(const FsInitArgs& args) {
190 Error error = Filesystem::Init(args);
191 if (error)
192 return error;
194 // Parse filesystem args.
195 for (StringMap_t::const_iterator iter = args.string_map.begin();
196 iter != args.string_map.end();
197 ++iter) {
198 if (iter->first == "SOURCE") {
199 url_root_ = iter->second;
200 is_blob_url_ = strncmp(url_root_.c_str(), "blob:", 5) == 0;
202 } else if (iter->first == "manifest") {
203 char* text;
204 error = LoadManifest(iter->second, &text);
205 if (error)
206 return error;
208 error = ParseManifest(text);
209 if (error) {
210 free(text);
211 return error;
214 free(text);
215 } else if (iter->first == "allow_cross_origin_requests") {
216 allow_cors_ = iter->second == "true";
217 } else if (iter->first == "allow_credentials") {
218 allow_credentials_ = iter->second == "true";
219 } else if (iter->first == "cache_stat") {
220 cache_stat_ = iter->second == "true";
221 } else if (iter->first == "cache_content") {
222 cache_content_ = iter->second == "true";
223 } else {
224 // Assume it is a header to pass to an HTTP request.
225 headers_[NormalizeHeaderKey(iter->first)] = iter->second;
229 if (!is_blob_url_) {
230 if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') {
231 // Make sure url_root_ ends with a slash, except for blob URLs.
232 url_root_ += '/';
235 ScopedNode root;
236 error = FindOrCreateDir(Path("/"), &root);
237 if (error)
238 return error;
241 return 0;
244 void HttpFs::Destroy() {
247 Error HttpFs::FindOrCreateDir(const Path& path, ScopedNode* out_node) {
248 out_node->reset(NULL);
250 ScopedNode node = FindExistingNode(path);
251 if (node.get() != NULL) {
252 *out_node = node;
253 return 0;
256 // If the node does not exist, create it.
257 node.reset(new DirNode(this, S_IRALL | S_IXALL));
258 Error error = node->Init(0);
259 if (error)
260 return error;
262 // If not the root node, find the parent node and add it to the parent
263 if (!path.IsRoot()) {
264 ScopedNode parent;
265 error = FindOrCreateDir(path.Parent(), &parent);
266 if (error)
267 return error;
269 error = parent->AddChild(path.Basename(), node);
270 if (error)
271 return error;
274 // Add it to the node cache.
275 node_cache_[path.Join()] = node;
276 *out_node = node;
277 return 0;
280 Error HttpFs::ParseManifest(const char* text) {
281 std::vector<std::string> lines;
282 sdk_util::SplitString(text, '\n', &lines);
284 for (size_t i = 0; i < lines.size(); i++) {
285 std::vector<std::string> words;
286 sdk_util::SplitString(lines[i], ' ', &words);
288 // Remove empty words (due to multiple consecutive spaces).
289 std::vector<std::string> non_empty_words;
290 for (std::vector<std::string>::const_iterator it = words.begin();
291 it != words.end();
292 ++it) {
293 if (!it->empty())
294 non_empty_words.push_back(*it);
297 if (non_empty_words.size() == 3) {
298 const std::string& modestr = non_empty_words[0];
299 const std::string& lenstr = non_empty_words[1];
300 const std::string& name = non_empty_words[2];
302 assert(modestr.size() == 4);
303 assert(name[0] == '/');
305 // Only support regular and streams for now
306 // Ignore EXEC bit
307 int type = 0;
308 switch (modestr[0]) {
309 case '-':
310 type = S_IFREG;
311 break;
312 case 'c':
313 type = S_IFCHR;
314 break;
315 default:
316 LOG_ERROR("Unable to parse type %s for %s.",
317 modestr.c_str(),
318 name.c_str());
319 return EINVAL;
322 int mode = 0;
323 switch (modestr[1]) {
324 case '-':
325 break;
326 case 'r':
327 mode |= S_IRUSR | S_IRGRP | S_IROTH;
328 break;
329 default:
330 LOG_ERROR("Unable to parse read %s for %s.",
331 modestr.c_str(),
332 name.c_str());
333 return EINVAL;
336 switch (modestr[2]) {
337 case '-':
338 break;
339 case 'w':
340 mode |= S_IWUSR | S_IWGRP | S_IWOTH;
341 break;
342 default:
343 LOG_ERROR("Unable to parse write %s for %s.",
344 modestr.c_str(),
345 name.c_str());
346 return EINVAL;
349 Path path(name);
350 std::string url = MakeUrl(path);
352 HttpFsNode* http_node = new HttpFsNode(this, url, cache_content_);
353 ScopedNode node(http_node);
354 node->SetMode(mode);
355 node->SetType(type);
357 Error error = node->Init(0);
358 if (error)
359 return error;
360 http_node->SetCachedSize(atoi(lenstr.c_str()));
362 ScopedNode dir_node;
363 error = FindOrCreateDir(path.Parent(), &dir_node);
364 if (error)
365 return error;
367 error = dir_node->AddChild(path.Basename(), node);
368 if (error)
369 return error;
371 node_cache_[path.Join()] = node;
375 return 0;
378 Error HttpFs::LoadManifest(const std::string& manifest_name,
379 char** out_manifest) {
380 Path manifest_path(manifest_name);
381 ScopedNode manifest_node;
382 *out_manifest = NULL;
384 int error = Open(manifest_path, O_RDONLY, &manifest_node);
385 if (error)
386 return error;
388 off_t size;
389 error = manifest_node->GetSize(&size);
390 if (error)
391 return error;
393 char* text = (char*)malloc(size + 1);
394 assert(text != NULL);
395 if (text == NULL)
396 return ENOMEM;
397 int len;
398 error = manifest_node->Read(HandleAttr(), text, size, &len);
399 if (error)
400 return error;
402 text[len] = 0;
403 *out_manifest = text;
404 return 0;
407 std::string HttpFs::MakeUrl(const Path& path) {
408 return url_root_ + Path(path).MakeRelative().Join();
411 } // namespace nacl_io