Skip to content
Browse files

add readonly support; vpath enhancements

  • Loading branch information...
1 parent df02dcb commit d9c20ddfe9b8960ec29a32ddf50ebc69a94f34a5 @omphalos committed Aug 4, 2012
Showing with 104 additions and 99 deletions.
  1. +2 −2 bin/crud-file-server
  2. +102 −97 crud-file-server.js
View
4 bin/crud-file-server
@@ -24,11 +24,11 @@ if(!argv.q) {
var port = argv.p || 80;
var path = argv.f || process.cwd();
-var vpath = argv.v || '';
+var vpath = (argv.v || '').trimLeft();
require('http').createServer(function (req, res) {
server.handleRequest(vpath, path, req, res);
}).listen(port);
console.log('server started');
-console.log('listening on ' + vpath + ':' + port + ', serving directory ' + path);
+console.log('listening on :' + port + '/' + vpath + ', serving ' + path);
View
199 crud-file-server.js
@@ -13,9 +13,10 @@ example usage:
server.handleRequest(port, path, req, res, vpath);
}).listen(port);
*/
-exports.handleRequest = function(vpath, path, req, res) {
+exports.handleRequest = function(vpath, path, req, res, readOnly) {
// vpath: (optional) virtual path to host in the url
// path: the file system path to serve
+ // readOnly: whether to allow modifications to the file
// our error handler
var writeError = function (err, code) {
@@ -41,7 +42,7 @@ exports.handleRequest = function(vpath, path, req, res) {
if(url[0] === '/') { url = url.slice(1, url.length); }
// check that url begins with vpath
- if(vpath && url.indexOf(vpath + '/') != 0) {
+ if(vpath && url.indexOf(vpath) != 0) {
console.log('url does not begin with vpath');
throw 'url [' + url + '] does not begin with vpath [' + vpath + ']';
}
@@ -53,109 +54,113 @@ exports.handleRequest = function(vpath, path, req, res) {
console.log('relativePath: ' + relativePath);
try {
- switch(req.method) {
- case 'GET': // returns file or directory contents
- if(url === 'favicon.ico') {
- res.end(); // if the browser requests favicon, just return an empty response
- } else {
- fs.stat(relativePath, function(err, stats) { // determine if the resource is a file or directory
+ if(readOnly && req.method != 'GET') {
+ writeError(req.method + ' forbidden on this resource', 403);
+ } else {
+ switch(req.method) {
+ case 'GET': // returns file or directory contents
+ if(url === 'favicon.ico') {
+ res.end(); // if the browser requests favicon, just return an empty response
+ } else {
+ fs.stat(relativePath, function(err, stats) { // determine if the resource is a file or directory
+ if(err) { writeError(err); }
+ else {
+ if(stats.isDirectory()) {
+ // if it's a directory, return the files as a JSONified array
+ console.log('reading directory ' + relativePath);
+ fs.readdir(relativePath, function(err, files) {
+ if(err) { writeError(err); }
+ else {
+ res.setHeader('Content-Type', 'application/json');
+ res.end(JSON.stringify(files));
+ }
+ });
+ } else {
+ // if it's a file, return the contents of a file with the correct content type
+ console.log('reading file ' + relativePath);
+ var type = require('mime').lookup(relativePath);
+ res.setHeader('Content-Type', type);
+ fs.readFile(relativePath, function(err, data) {
+ if(err) { writeError(err); }
+ else {
+ res.end(data);
+ }
+ });
+ }
+ }
+ });
+ }
+ return;
+ case 'PUT': // write a file
+ console.log('writing ' + relativePath);
+ var stream = fs.createWriteStream(relativePath);
+ stream.ok = true;
+ req.pipe(stream); // TODO: limit data length
+ req.on('end', function() {
+ if(stream.ok) {
+ res.end();
+ }
+ });
+ stream.on('error', function(err) {
+ stream.ok = false;
+ writeError(err);
+ });
+ return;
+ case 'POST': // create a directory or rename a file or directory
+ if(query.rename) { // rename a file or directory
+ // e.g., http://localhost/old-name.html?rename=new-name.html
+ query.rename = cleanUrl(query.rename);
+ if(vpath) {
+ if(query.rename.indexOf('/' + vpath + '/') == 0) {
+ query.rename = query.rename.slice(vpath.length + 2, query.rename.length);
+ } else {
+ throw 'renamed url [' + query.rename + '] does not begin with vpath [' + vpath + ']';
+ }
+ }
+ console.log('renaming ' + relativePath + ' to ' + path + query.rename);
+ fs.rename(relativePath, path + query.rename, function(err) {
+ if(err) { writeError(err); }
+ else {
+ res.end();
+ }
+ });
+ } else if(query.create == 'directory') { // rename a directory
+ // e.g., http://localhost/new-directory?create=directory
+ console.log('creating directory ' + relativePath);
+ fs.mkdir(relativePath, 0777, function(err) {
+ if(err) { writeError(err); }
+ else {
+ res.end();
+ }
+ });
+ } else {
+ writeError('valid queries are ' + url + '?rename=[new name] or ' + url + '?create=directory');
+ }
+ return;
+ case 'DELETE': // delete a file or directory
+ fs.stat(relativePath, function(err, stats) {
if(err) { writeError(err); }
else {
- if(stats.isDirectory()) {
- // if it's a directory, return the files as a JSONified array
- console.log('reading directory ' + relativePath);
- fs.readdir(relativePath, function(err, files) {
+ if(stats.isDirectory()) { // delete a directory
+ console.log('deleting directory ' + relativePath);
+ fs.rmdir(relativePath, function(err) {
if(err) { writeError(err); }
- else {
- res.setHeader('Content-Type', 'application/json');
- res.end(JSON.stringify(files));
- }
+ else { res.end(); }
});
- } else {
- // if it's a file, return the contents of a file with the correct content type
- console.log('reading file ' + relativePath);
- var type = require('mime').lookup(relativePath);
- res.setHeader('Content-Type', type);
- fs.readFile(relativePath, function(err, data) {
+ } else { // delete a file
+ console.log('deleting file ' + relativePath);
+ fs.unlink(relativePath, function(err) {
if(err) { writeError(err); }
- else {
- res.end(data);
- }
+ else { res.end(); }
});
}
}
- });
- }
- return;
- case 'PUT': // write a file
- console.log('writing ' + relativePath);
- var stream = fs.createWriteStream(relativePath);
- stream.ok = true;
- req.pipe(stream); // TODO: limit data length
- req.on('end', function() {
- if(stream.ok) {
- res.end();
- }
- });
- stream.on('error', function(err) {
- stream.ok = false;
- writeError(err);
- });
- return;
- case 'POST': // create a directory or rename a file or directory
- if(query.rename) { // rename a file or directory
- // e.g., http://localhost/old-name.html?rename=new-name.html
- query.rename = cleanUrl(query.rename);
- if(vpath) {
- if(query.rename.indexOf('/' + vpath + '/') == 0) {
- query.rename = query.rename.slice(vpath.length + 2, query.rename.length);
- } else {
- throw 'renamed url [' + query.rename + '] does not begin with vpath [' + vpath + ']';
- }
- }
- console.log('renaming ' + relativePath + ' to ' + path + query.rename);
- fs.rename(relativePath, path + query.rename, function(err) {
- if(err) { writeError(err); }
- else {
- res.end();
- }
- });
- } else if(query.create == 'directory') { // rename a directory
- // e.g., http://localhost/new-directory?create=directory
- console.log('creating directory ' + relativePath);
- fs.mkdir(relativePath, 0777, function(err) {
- if(err) { writeError(err); }
- else {
- res.end();
- }
- });
- } else {
- writeError('valid queries are ' + url + '?rename=[new name] or ' + url + '?create=directory');
- }
- return;
- case 'DELETE': // delete a file or directory
- fs.stat(relativePath, function(err, stats) {
- if(err) { writeError(err); }
- else {
- if(stats.isDirectory()) { // delete a directory
- console.log('deleting directory ' + relativePath);
- fs.rmdir(relativePath, function(err) {
- if(err) { writeError(err); }
- else { res.end(); }
- });
- } else { // delete a file
- console.log('deleting file ' + relativePath);
- fs.unlink(relativePath, function(err) {
- if(err) { writeError(err); }
- else { res.end(); }
- });
- }
- }
- });
- return;
- default: // unsupported method! tell the client ...
- writeError('Method ' + method + ' not allowed', 405);
- return;
+ });
+ return;
+ default: // unsupported method! tell the client ...
+ writeError('Method ' + method + ' not allowed', 405);
+ return;
+ }
}
} catch(err) {
// file system ('fs') errors are just bubbled up to this error handler

0 comments on commit d9c20dd

Please sign in to comment.
Something went wrong with that request. Please try again.