1 // Copyright (c) 2012 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/mount_http.h"
14 #include <sys/types.h>
18 #include "nacl_io/kernel_handle.h"
19 #include "nacl_io/mount_node_dir.h"
20 #include "nacl_io/mount_node_http.h"
21 #include "nacl_io/osinttypes.h"
22 #include "nacl_io/osunistd.h"
23 #include <ppapi/c/pp_errors.h>
24 #include "sdk_util/string_util.h"
28 std::string NormalizeHeaderKey(const std::string& s) {
29 // Capitalize the first letter and any letter following a hyphen:
30 // e.g. ACCEPT-ENCODING -> Accept-Encoding
33 for (size_t i = 0; i < s.length(); ++i) {
35 result += upper ? toupper(c) : tolower(c);
42 Error MountHttp::Access(const Path& path, int a_mode) {
43 assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
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 ScopedMountNode node(new MountNodeHttp(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 MountHttp::Open(const Path& path, int open_flags,
67 ScopedMountNode* out_node) {
68 out_node->reset(NULL);
69 assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
71 NodeMap_t::iterator iter = node_cache_.find(path.Join());
72 if (iter != node_cache_.end()) {
73 *out_node = iter->second;
77 // If we can't find the node in the cache, create it
78 std::string url = MakeUrl(path);
79 ScopedMountNode node(new MountNodeHttp(this, url, cache_content_));
80 Error error = node->Init(open_flags);
84 error = node->GetStat(NULL);
88 ScopedMountNode parent;
89 error = FindOrCreateDir(path.Parent(), &parent);
93 error = parent->AddChild(path.Basename(), node);
97 node_cache_[path.Join()] = node;
102 Error MountHttp::Unlink(const Path& path) {
103 NodeMap_t::iterator iter = node_cache_.find(path.Join());
104 if (iter == node_cache_.end())
107 if (iter->second->IsaDir())
113 Error MountHttp::Mkdir(const Path& path, int permissions) {
114 NodeMap_t::iterator iter = node_cache_.find(path.Join());
115 if (iter != node_cache_.end()) {
116 if (iter->second->IsaDir())
122 Error MountHttp::Rmdir(const Path& path) {
123 NodeMap_t::iterator iter = node_cache_.find(path.Join());
124 if (iter == node_cache_.end())
127 if (!iter->second->IsaDir())
133 Error MountHttp::Remove(const Path& path) {
134 NodeMap_t::iterator iter = node_cache_.find(path.Join());
135 if (iter == node_cache_.end())
141 PP_Resource MountHttp::MakeUrlRequestInfo(const std::string& url,
143 StringMap_t* additional_headers) {
144 URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface();
145 VarInterface* var_interface = ppapi_->GetVarInterface();
147 PP_Resource request_info = interface->Create(ppapi_->GetInstance());
151 interface->SetProperty(request_info,
152 PP_URLREQUESTPROPERTY_URL,
153 var_interface->VarFromUtf8(url.c_str(), url.length()));
154 interface->SetProperty(request_info,
155 PP_URLREQUESTPROPERTY_METHOD,
156 var_interface->VarFromUtf8(method, strlen(method)));
157 interface->SetProperty(request_info,
158 PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
159 PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE));
160 interface->SetProperty(request_info,
161 PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
162 PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE));
164 // Merge the mount headers with the request headers. If the field is already
165 // set it |additional_headers|, don't use the one from headers_.
166 for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end();
168 const std::string& key = NormalizeHeaderKey(iter->first);
169 if (additional_headers->find(key) == additional_headers->end()) {
170 additional_headers->insert(std::make_pair(key, iter->second));
174 // Join the headers into one string.
176 for (StringMap_t::iterator iter = additional_headers->begin();
177 iter != additional_headers->end();
179 headers += iter->first + ": " + iter->second + '\n';
182 interface->SetProperty(
184 PP_URLREQUESTPROPERTY_HEADERS,
185 var_interface->VarFromUtf8(headers.c_str(), headers.length()));
190 MountHttp::MountHttp()
191 : allow_cors_(false),
192 allow_credentials_(false),
194 cache_content_(true) {}
196 Error MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
197 Error error = Mount::Init(dev, args, ppapi);
202 for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) {
203 if (iter->first == "SOURCE") {
204 url_root_ = iter->second;
206 // Make sure url_root_ ends with a slash.
207 if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') {
210 } else if (iter->first == "manifest") {
212 error = LoadManifest(iter->second, &text);
216 error = ParseManifest(text);
223 } else if (iter->first == "allow_cross_origin_requests") {
224 allow_cors_ = iter->second == "true";
225 } else if (iter->first == "allow_credentials") {
226 allow_credentials_ = iter->second == "true";
227 } else if (iter->first == "cache_stat") {
228 cache_stat_ = iter->second == "true";
229 } else if (iter->first == "cache_content") {
230 cache_content_ = iter->second == "true";
232 // Assume it is a header to pass to an HTTP request.
233 headers_[NormalizeHeaderKey(iter->first)] = iter->second;
240 void MountHttp::Destroy() {}
242 Error MountHttp::FindOrCreateDir(const Path& path,
243 ScopedMountNode* out_node) {
244 out_node->reset(NULL);
245 std::string strpath = path.Join();
246 NodeMap_t::iterator iter = node_cache_.find(strpath);
247 if (iter != node_cache_.end()) {
248 *out_node = iter->second;
252 // If the node does not exist, create it.
253 ScopedMountNode node(new MountNodeDir(this));
254 Error error = node->Init(0);
258 // If not the root node, find the parent node and add it to the parent
260 ScopedMountNode parent;
261 error = FindOrCreateDir(path.Parent(), &parent);
265 error = parent->AddChild(path.Basename(), node);
270 // Add it to the node cache.
271 node_cache_[strpath] = node;
276 Error MountHttp::ParseManifest(const char* text) {
277 std::vector<std::string> lines;
278 sdk_util::SplitString(text, '\n', &lines);
280 for (size_t i = 0; i < lines.size(); i++) {
281 std::vector<std::string> words;
282 sdk_util::SplitString(lines[i], ' ', &words);
284 // Remove empty words (due to multiple consecutive spaces).
285 std::vector<std::string> non_empty_words;
286 for (std::vector<std::string>::const_iterator it = words.begin();
287 it != words.end(); ++it) {
289 non_empty_words.push_back(*it);
292 if (non_empty_words.size() == 3) {
293 const std::string& modestr = non_empty_words[0];
294 const std::string& lenstr = non_empty_words[1];
295 const std::string& name = non_empty_words[2];
297 assert(modestr.size() == 4);
298 assert(name[0] == '/');
300 // Only support regular and streams for now
303 switch (modestr[0]) {
311 fprintf(stderr, "Unable to parse type %s for %s.\n", modestr.c_str(),
316 switch (modestr[1]) {
320 mode |= S_IRUSR | S_IRGRP | S_IROTH;
323 fprintf(stderr, "Unable to parse read %s for %s.\n", modestr.c_str(),
328 switch (modestr[2]) {
332 mode |= S_IWUSR | S_IWGRP | S_IWOTH;
335 fprintf(stderr, "Unable to parse write %s for %s.\n", modestr.c_str(),
341 std::string url = MakeUrl(path);
343 MountNodeHttp* http_node = new MountNodeHttp(this, url, cache_content_);
344 http_node->SetMode(mode);
345 ScopedMountNode node(http_node);
347 Error error = node->Init(0);
350 http_node->SetCachedSize(atoi(lenstr.c_str()));
352 ScopedMountNode dir_node;
353 error = FindOrCreateDir(path.Parent(), &dir_node);
357 error = dir_node->AddChild(path.Basename(), node);
361 std::string pname = path.Join();
362 node_cache_[pname] = node;
369 Error MountHttp::LoadManifest(const std::string& manifest_name,
370 char** out_manifest) {
371 Path manifest_path(manifest_name);
372 ScopedMountNode manifest_node;
373 *out_manifest = NULL;
375 int error = Open(manifest_path, O_RDONLY, &manifest_node);
380 error = manifest_node->GetSize(&size);
384 char* text = new char[size + 1];
386 error = manifest_node->Read(HandleAttr(), text, size, &len);
391 *out_manifest = text;
395 std::string MountHttp::MakeUrl(const Path& path) {
397 (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join());
400 } // namespace nacl_io