Comparing version 0.0.0 to 0.0.1
var express = require('express'); | ||
var ecstatic = require('../')(__dirname + '/public'); | ||
var ecstatic = require('../lib/ecstatic'); | ||
var app = express.createServer(); | ||
app.use(ecstatic); | ||
app.use(ecstatic(__dirname + '/public')); | ||
app.use(ecstatic.showDir(__dirname + '/public')); | ||
app.listen(8080); | ||
console.log('Listening on :8080'); |
var union = require('union'); | ||
var ecstatic = require('../')(__dirname + '/public'); | ||
var ecstatic = require('../'); | ||
union.createServer({ | ||
before: [ | ||
ecstatic | ||
ecstatic(__dirname + '/public'), | ||
ecstatic.showDir(__dirname + '/public') | ||
] | ||
@@ -8,0 +9,0 @@ }).listen(8080); |
@@ -5,14 +5,12 @@ var path = require('path'), | ||
mime = require('mime'), | ||
showDir = require('./ecstatic/showdir'); | ||
showDir = require('./ecstatic/showdir'), | ||
version = JSON.parse( | ||
fs.readFileSync(__dirname + '/../package.json').toString() | ||
).version, | ||
status = require('./ecstatic/status-handlers'), | ||
etag = require('./ecstatic/etag'); | ||
// TODO: | ||
// | ||
// * Server info (ecstatic-0.0.0) | ||
var cache = 3600; // cache-ing time in seconds | ||
exports.showDir = showDir; | ||
module.exports = function (dir) { | ||
var root = path.resolve(dir) + '/'; | ||
var ecstatic = module.exports = function (dir, opts) { | ||
var root = path.resolve(dir) + '/', | ||
cache = (opts && opts.cache) || 3600; // cache-ing time in seconds. | ||
@@ -22,87 +20,75 @@ return function middleware (req, res, next) { | ||
var parsed = url.parse(req.url), | ||
pathname = decodeURI(parsed.pathname), | ||
file = path.normalize(path.join(root, parsed.pathname)); | ||
// 403 if the path goes outside the root. | ||
// Set common headers. | ||
res.setHeader('server', 'ecstatic-'+version); | ||
res.setHeader('date', (new Date()).toUTCString()); | ||
if (file.slice(0, root.length) !== root) { | ||
if (next) { | ||
next(); | ||
} | ||
else { | ||
res.statusCode = 403; | ||
if (res.writable) { | ||
res.setHeader('content-type', 'text/plain'); | ||
res.end('ACCESS DENIED'); | ||
} | ||
} | ||
return; | ||
return status[403](res, next); | ||
} | ||
// Read in the file! | ||
if (req.method && (req.method !== 'GET' && req.method !== 'HEAD' )) { | ||
return status[405](res, next); | ||
} | ||
fs.stat(file, function (err, stat) { | ||
if (err && err.code === 'ENOENT') { | ||
if (typeof next === 'function') { | ||
next(); | ||
} | ||
else { | ||
res.statusCode = 404; | ||
if (res.writable) { | ||
res.setHeader('content-type', 'text/plain'); | ||
// TODO: Make pluggable. | ||
res.end('File not found. :('); | ||
} | ||
} | ||
status[404](res, next); | ||
} | ||
else if (err) { | ||
res.statusCode = 500; | ||
// TODO: Don't share the error code with the user. | ||
res.end(err.stack || err.toString()); | ||
status[500](res, next, { error: err }); | ||
} | ||
else if (stat.isDirectory()) { | ||
// Directory view! | ||
var req_ = { | ||
url : path.join(parsed.pathname, '/index.html') | ||
}; | ||
// Pass the new url to our middleware. | ||
// Use 'next' if it exists, otherwise our fancy-schmancy | ||
// "showdir" middleware. | ||
var handler = (typeof next === 'function') | ||
? next | ||
: function () { | ||
showDir(file, parsed.pathname, res); | ||
showDir(file, pathname, stat, cache)(req, res); | ||
}; | ||
middleware(req_, res, handler); | ||
middleware({ | ||
url: path.join(pathname, '/index.html') | ||
}, res, handler); | ||
} | ||
else { | ||
var etag = JSON.stringify([stat.ino, stat.size, stat.mtime].join('-')); | ||
// TODO: Helper for this, with default headers. | ||
res.setHeader('content-type', mime.lookup(file) || 'application/octet-stream'); | ||
res.setHeader('etag', etag); | ||
res.setHeader('date', (new Date()).toUTCString()); | ||
res.setHeader('etag', etag(stat)); | ||
res.setHeader('last-modified', (new Date(stat.mtime)).toUTCString()); | ||
res.setHeader('cache-control', 'max-age='+cache); | ||
res.setHeader('server', 'ecstatic-0.0.0'); | ||
// Return a 304 if necessary | ||
if ( (req.headers['if-none-match'] === etag) | ||
|| (Date.parse(req.headers['if-none-match']) >= stat.mtime ) ) { | ||
res.statusCode = 304; | ||
res.end(); | ||
if ( req.headers | ||
&& ( (req.headers['if-none-match'] === etag) | ||
|| (Date.parse(req.headers['if-none-match']) >= stat.mtime ) | ||
) | ||
) { | ||
status[304](res, next); | ||
} | ||
else { | ||
// Serve it up! | ||
var stream = fs.createReadStream(file); | ||
res.setHeader( | ||
'content-type', | ||
mime.lookup(file) || 'application/octet-stream' | ||
); | ||
stream.pipe(res); | ||
stream.on('error', function (err) { | ||
res.statusCode = 500; | ||
if (res.writable) { | ||
res.end(err && err.stack || err.toString()); | ||
} | ||
}); | ||
if (req.method === "HEAD") { | ||
res.statusCode = 200; | ||
res.end(); | ||
} | ||
else { | ||
var stream = fs.createReadStream(file); | ||
stream.pipe(res); | ||
stream.on('error', function (err) { | ||
status['500'](res, next, { error: err }); | ||
}); | ||
stream.on('end', function () { | ||
res.statusCode = 200; | ||
res.end(); | ||
}); | ||
} | ||
} | ||
@@ -113,1 +99,5 @@ } | ||
}; | ||
ecstatic.version = version; | ||
ecstatic.showDir = showDir; | ||
@@ -1,42 +0,113 @@ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var ent = require('ent'); | ||
var ecstatic = require('../ecstatic'), | ||
fs = require('fs'), | ||
path = require('path'), | ||
ent = require('ent'), | ||
etag = require('./etag'), | ||
url = require('url'), | ||
status = require('./status-handlers'); | ||
module.exports = function (dir, pathname, res) { | ||
fs.readdir(dir, function (err, xs) { | ||
if (err) { | ||
res.statusCode = 500; | ||
res.end(err && err.stack || err.toString()); | ||
module.exports = function (dir, pathname, stat, cache) { | ||
var root = path.resolve(dir) + '/'; | ||
return function (req, res, next) { | ||
if (typeof pathname === 'undefined') { | ||
pathname = url.parse(req.url).pathname; | ||
} | ||
else { | ||
var pending = xs.length; | ||
var dirs = [], files = [], errs = []; | ||
function writeFile (file) { | ||
var p = path.join(pathname, file); | ||
res.write( | ||
'<div><a href="' + encodeURI(p) + '">' | ||
+ ent.encode(file) + '</a></div>' | ||
); | ||
if (typeof file === 'undefined') { | ||
file = path.normalize(path.join(root, pathname)); | ||
} | ||
if (typeof cache === 'undefined') { | ||
cache = 3600*1000; | ||
} | ||
(function (cb) { | ||
fs.stat(file, cb); | ||
})(function (err, stat) { | ||
if (err) { | ||
return status[500](res, next, { error: err }); | ||
} | ||
function finish () { | ||
dirs.sort().forEach(function (file) { | ||
writeFile(file + '/'); | ||
fs.readdir(dir + pathname, function (err, files) { | ||
if (err) { | ||
return status[500](res, next, { error: err }); | ||
} | ||
res.setHeader('content-type', 'text/html'); | ||
res.setHeader('etag', etag(stat)); | ||
res.setHeader('last-modified', (new Date(stat.mtime)).toUTCString()); | ||
res.setHeader('cache-control', 'max-age='+cache); | ||
var sortByIsDirectory = function (paths, cb) { | ||
var pending = paths.length, | ||
errs = [], | ||
dirs = [], | ||
files = []; | ||
paths.forEach(function (file) { | ||
fs.stat(dir + pathname + '/' + file, function (err, s) { | ||
if (err) { | ||
errs.push(err); | ||
} | ||
else if (s.isDirectory()) { | ||
dirs.push(file); | ||
} | ||
else { | ||
files.push(file); | ||
} | ||
if (--pending === 0) { | ||
cb(errs, dirs, files); | ||
} | ||
}); | ||
}); | ||
} | ||
sortByIsDirectory(files, function (errs, dirs, files) { | ||
if (errs.length > 0) { | ||
return status[500](res, next, { error: errs[0] }); | ||
} | ||
// Lifted from nodejitsu's http server. | ||
var html = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\ | ||
"http://www.w3.org/TR/html4/loose.dtd">\ | ||
<html> \ | ||
<head> \ | ||
<title>Index of ' + pathname +'</title> \ | ||
</head> \ | ||
<body> \ | ||
<h1>Index of ' + pathname + '</h1>'; | ||
html += '<table>'; | ||
var writeRow = function (file, i) { | ||
html += '<tr><td>' + '<a href="' | ||
+ ent.encode(encodeURI( | ||
((req.url == '/') ? '' : req.url) | ||
+ '/' | ||
+ file | ||
)) + '">' + ent.encode(file) + '</a></td></tr>'; | ||
} | ||
dirs.sort().forEach(writeRow); | ||
files.sort().forEach(writeRow); | ||
html += '</table>'; | ||
html += '<br><address>Node.js ' | ||
+ process.version | ||
+ '/<a href="https://github.com/jesusabdullah/node-ecstatic">ecstatic</a>' | ||
+ ' server running @ ' | ||
+ ent.encode(req.headers.host) + '</address>' | ||
+ '</body></html>' | ||
; | ||
res.writeHead(200, { "Content-Type": "text/html" }); | ||
res.end(html); | ||
}); | ||
files.sort().forEach(writeFile); | ||
res.end(); | ||
} | ||
xs.forEach(function (file) { | ||
fs.stat(dir + '/' + file, function (err, s) { | ||
if (err) errs.push(err) | ||
else if (s.isDirectory()) dirs.push(file) | ||
else files.push(file) | ||
if (--pending === 0) finish() | ||
}); | ||
}); | ||
} | ||
}); | ||
}); | ||
} | ||
}; |
@@ -5,3 +5,3 @@ { | ||
"description": "A simple static file server middleware that works with both Express and Flatiron", | ||
"version": "0.0.0", | ||
"version": "0.0.1", | ||
"homepage": "https://github.com/jesusabdullah/node-ecstatic", | ||
@@ -21,3 +21,4 @@ "repository": { | ||
"files", | ||
"mime" | ||
"mime", | ||
"middleware" | ||
], | ||
@@ -36,8 +37,3 @@ "engines": { | ||
"union" : "0.1.x" | ||
}, | ||
"directories" : { | ||
"test" : "test", | ||
"example" : "example", | ||
"lib" : "." | ||
} | ||
} |
@@ -5,5 +5,5 @@ # Ecstatic | ||
* simple directory listings | ||
* show index.html files at directory roots when they exist | ||
* use it with a raw http server, connect/express, or flatiron/union | ||
* Built-in simple directory listings | ||
* Shows index.html files at directory roots when they exist | ||
* Use it with a raw http server, express/connect, or flatiron/union! | ||
@@ -16,6 +16,7 @@ # Examples: | ||
var express = require('express'); | ||
var ecstatic = require('../')(__dirname + '/public'); | ||
var ecstatic = require('../');; | ||
var app = express.createServer(); | ||
app.use(ecstatic); | ||
app.use(ecstatic(__dirname + '/public')); | ||
app.use(ecstatic.showdir(__dirname + '/public')); | ||
app.listen(8080); | ||
@@ -30,7 +31,8 @@ | ||
var union = require('union'); | ||
var ecstatic = require('../')(__dirname + '/public'); | ||
var ecstatic = require('../'); | ||
union.createServer({ | ||
before: [ | ||
ecstatic | ||
ecstatic(__dirname + '/public'), | ||
ecstatic.showdir(__dirname + '/public') | ||
] | ||
@@ -48,6 +50,10 @@ }).listen(8080); | ||
## middleware(req, res, next); | ||
### middleware(req, res, next); | ||
This works more or less as you'd expect. | ||
## ecstatic.showdir(folder); | ||
This returns another middleware which will attempt to show a directory view. At the moment, you must add this explicitly for union and connect middleware stacks, so that one may chose actions other than showing a directory view if desired. | ||
# Tests: | ||
@@ -59,8 +65,6 @@ | ||
This project's implementation is pretty much g2g (thanks @substack) but currently does not work in Union. This is because Union's response piping is currently broken. | ||
This is still "beta" quality, and you may find bugs. Please give me a heads-up if you find any! Pull requests encouraged. | ||
Once Union is fixed, this will work there as well. | ||
# License: | ||
MIT/X11. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
207740
19
315
66
2