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::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
) {
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
);
61 error
= node
->GetStat(NULL
);
66 error
= FindOrCreateDir(path
.Parent(), &parent
);
70 error
= parent
->AddChild(path
.Basename(), node
);
74 node_cache_
[path
.Join()] = node
;
79 ScopedNode
HttpFs::FindExistingNode(const Path
& path
) {
80 NodeMap_t::iterator iter
= node_cache_
.find(path
.Join());
81 if (iter
== node_cache_
.end())
86 Error
HttpFs::Unlink(const Path
& path
) {
87 ScopedNode node
= FindExistingNode(path
);
88 if (node
.get() == NULL
)
97 Error
HttpFs::Mkdir(const Path
& path
, int permissions
) {
98 ScopedNode node
= FindExistingNode(path
);
99 if (node
.get() != NULL
&& node
->IsaDir())
105 Error
HttpFs::Rmdir(const Path
& path
) {
106 ScopedNode node
= FindExistingNode(path
);
107 if (node
.get() == NULL
)
116 Error
HttpFs::Remove(const Path
& path
) {
117 ScopedNode node
= FindExistingNode(path
);
118 if (node
.get() == NULL
)
124 Error
HttpFs::Rename(const Path
& path
, const Path
& newpath
) {
125 ScopedNode node
= FindExistingNode(path
);
126 if (node
.get() == NULL
)
132 PP_Resource
HttpFs::MakeUrlRequestInfo(const std::string
& url
,
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());
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();
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.
167 for (StringMap_t::iterator iter
= additional_headers
->begin();
168 iter
!= additional_headers
->end();
170 headers
+= iter
->first
+ ": " + iter
->second
+ '\n';
173 interface
->SetProperty(
175 PP_URLREQUESTPROPERTY_HEADERS
,
176 var_interface
->VarFromUtf8(headers
.c_str(), headers
.length()));
182 : allow_cors_(false),
183 allow_credentials_(false),
185 cache_content_(true),
186 is_blob_url_(false) {
189 Error
HttpFs::Init(const FsInitArgs
& args
) {
190 Error error
= Filesystem::Init(args
);
194 // Parse filesystem args.
195 for (StringMap_t::const_iterator iter
= args
.string_map
.begin();
196 iter
!= args
.string_map
.end();
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") {
204 error
= LoadManifest(iter
->second
, &text
);
208 error
= ParseManifest(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";
224 // Assume it is a header to pass to an HTTP request.
225 headers_
[NormalizeHeaderKey(iter
->first
)] = iter
->second
;
230 if (!url_root_
.empty() && url_root_
[url_root_
.length() - 1] != '/') {
231 // Make sure url_root_ ends with a slash, except for blob URLs.
236 error
= FindOrCreateDir(Path("/"), &root
);
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
) {
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);
262 // If not the root node, find the parent node and add it to the parent
263 if (!path
.IsRoot()) {
265 error
= FindOrCreateDir(path
.Parent(), &parent
);
269 error
= parent
->AddChild(path
.Basename(), node
);
274 // Add it to the node cache.
275 node_cache_
[path
.Join()] = node
;
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();
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
308 switch (modestr
[0]) {
316 LOG_ERROR("Unable to parse type %s for %s.",
323 switch (modestr
[1]) {
327 mode
|= S_IRUSR
| S_IRGRP
| S_IROTH
;
330 LOG_ERROR("Unable to parse read %s for %s.",
336 switch (modestr
[2]) {
340 mode
|= S_IWUSR
| S_IWGRP
| S_IWOTH
;
343 LOG_ERROR("Unable to parse write %s for %s.",
350 std::string url
= MakeUrl(path
);
352 HttpFsNode
* http_node
= new HttpFsNode(this, url
, cache_content_
);
353 ScopedNode
node(http_node
);
357 Error error
= node
->Init(0);
360 http_node
->SetCachedSize(atoi(lenstr
.c_str()));
363 error
= FindOrCreateDir(path
.Parent(), &dir_node
);
367 error
= dir_node
->AddChild(path
.Basename(), node
);
371 node_cache_
[path
.Join()] = node
;
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
);
389 error
= manifest_node
->GetSize(&size
);
393 char* text
= (char*)malloc(size
+ 1);
394 assert(text
!= NULL
);
398 error
= manifest_node
->Read(HandleAttr(), text
, size
, &len
);
403 *out_manifest
= text
;
407 std::string
HttpFs::MakeUrl(const Path
& path
) {
408 return url_root_
+ Path(path
).MakeRelative().Join();
411 } // namespace nacl_io