all da files
This commit is contained in:
227
node_modules/ecstatic/lib/ecstatic.js
generated
vendored
Executable file
227
node_modules/ecstatic/lib/ecstatic.js
generated
vendored
Executable file
@@ -0,0 +1,227 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
url = require('url'),
|
||||
mime = require('mime'),
|
||||
showDir = require('./ecstatic/showdir'),
|
||||
version = JSON.parse(
|
||||
fs.readFileSync(__dirname + '/../package.json').toString()
|
||||
).version,
|
||||
status = require('./ecstatic/status-handlers'),
|
||||
etag = require('./ecstatic/etag'),
|
||||
optsParser = require('./ecstatic/opts');
|
||||
|
||||
var ecstatic = module.exports = function (dir, options) {
|
||||
if (typeof dir !== 'string') {
|
||||
options = dir;
|
||||
dir = options.root;
|
||||
}
|
||||
|
||||
var root = path.join(path.resolve(dir), '/'),
|
||||
opts = optsParser(options),
|
||||
cache = opts.cache,
|
||||
autoIndex = opts.autoIndex,
|
||||
baseDir = opts.baseDir,
|
||||
defaultExt = opts.defaultExt;
|
||||
|
||||
opts.root = dir;
|
||||
|
||||
return function middleware (req, res, next) {
|
||||
|
||||
// Figure out the path for the file from the given url
|
||||
var parsed = url.parse(req.url);
|
||||
try {
|
||||
var pathname = decodeURI(parsed.pathname);
|
||||
}
|
||||
catch (err) {
|
||||
return status[400](res, next, { error: err });
|
||||
}
|
||||
|
||||
var file = path.normalize(
|
||||
path.join(root,
|
||||
path.relative(
|
||||
path.join('/', baseDir),
|
||||
pathname
|
||||
)
|
||||
)
|
||||
),
|
||||
gzipped = file + '.gz';
|
||||
|
||||
// Set common headers.
|
||||
res.setHeader('server', 'ecstatic-'+version);
|
||||
|
||||
// TODO: This check is broken, which causes the 403 on the
|
||||
// expected 404.
|
||||
if (file.slice(0, root.length) !== root) {
|
||||
return status[403](res, next);
|
||||
}
|
||||
|
||||
if (req.method && (req.method !== 'GET' && req.method !== 'HEAD' )) {
|
||||
return status[405](res, next);
|
||||
}
|
||||
|
||||
// Look for a gzipped file if this is turned on
|
||||
if (opts.gzip && shouldCompress(req)) {
|
||||
fs.stat(gzipped, function (err, stat) {
|
||||
if (!err && stat.isFile()) {
|
||||
file = gzipped;
|
||||
return serve(stat);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fs.stat(file, function (err, stat) {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
if (req.statusCode == 404) {
|
||||
// This means we're already trying ./404.html
|
||||
status[404](res, next);
|
||||
}
|
||||
else if (defaultExt && !path.extname(req.url).length) {
|
||||
//
|
||||
// If no file extension is specified and there is a default extension
|
||||
// try that before rendering 404.html.
|
||||
//
|
||||
middleware({
|
||||
url: req.url + '.' + defaultExt
|
||||
}, res, next);
|
||||
}
|
||||
else {
|
||||
// Try for ./404.html
|
||||
middleware({
|
||||
url: '/' + path.join(baseDir, '404.html'),
|
||||
statusCode: 404 // Override the response status code
|
||||
}, res, next);
|
||||
}
|
||||
}
|
||||
else if (err) {
|
||||
status[500](res, next, { error: err });
|
||||
}
|
||||
else if (stat.isDirectory()) {
|
||||
// 302 to / if necessary
|
||||
if (!pathname.match(/\/$/)) {
|
||||
res.statusCode = 302;
|
||||
res.setHeader('location', pathname + '/');
|
||||
return res.end();
|
||||
}
|
||||
|
||||
if (autoIndex) {
|
||||
return middleware({
|
||||
url: path.join(pathname, '/index.html')
|
||||
}, res, function (err) {
|
||||
if (err) {
|
||||
return status[500](res, next, { error: err });
|
||||
}
|
||||
if (opts.showDir) {
|
||||
return showDir(opts, stat)(req, res);
|
||||
}
|
||||
|
||||
return status[403](res, next);
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.showDir) {
|
||||
return showDir(opts, stat)(req, res);
|
||||
}
|
||||
|
||||
status[404](res, next);
|
||||
|
||||
}
|
||||
else {
|
||||
serve(stat);
|
||||
}
|
||||
});
|
||||
|
||||
function serve(stat) {
|
||||
|
||||
// TODO: Helper for this, with default headers.
|
||||
res.setHeader('etag', etag(stat));
|
||||
res.setHeader('last-modified', (new Date(stat.mtime)).toUTCString());
|
||||
res.setHeader('cache-control', cache);
|
||||
|
||||
// Return a 304 if necessary
|
||||
if ( req.headers
|
||||
&& (
|
||||
(req.headers['if-none-match'] === etag(stat))
|
||||
|| (Date.parse(req.headers['if-modified-since']) >= stat.mtime)
|
||||
)
|
||||
) {
|
||||
return status[304](res, next);
|
||||
}
|
||||
|
||||
res.setHeader('content-length', stat.size);
|
||||
|
||||
// Do a MIME lookup, fall back to octet-stream and handle gzip
|
||||
// special case.
|
||||
var contentType = mime.lookup(file), charSet;
|
||||
|
||||
if (contentType) {
|
||||
charSet = mime.charsets.lookup(contentType);
|
||||
if (charSet) {
|
||||
contentType += '; charset=' + charSet;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.extname(file) === '.gz') {
|
||||
res.setHeader('Content-Encoding', 'gzip');
|
||||
|
||||
// strip gz ending and lookup mime type
|
||||
contentType = mime.lookup(path.basename(file, ".gz"));
|
||||
}
|
||||
|
||||
res.setHeader('content-type', contentType || 'application/octet-stream');
|
||||
|
||||
if (req.method === "HEAD") {
|
||||
res.statusCode = req.statusCode || 200; // overridden for 404's
|
||||
return res.end();
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ecstatic.version = version;
|
||||
ecstatic.showDir = showDir;
|
||||
|
||||
// Check to see if we should try to compress a file with gzip.
|
||||
function shouldCompress(req) {
|
||||
var headers = req.headers;
|
||||
|
||||
return headers && headers['accept-encoding'] &&
|
||||
headers['accept-encoding']
|
||||
.split(",")
|
||||
.some(function (el) {
|
||||
return ['*','compress', 'gzip', 'deflate'].indexOf(el) != -1;
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
if(!module.parent) {
|
||||
var http = require('http'),
|
||||
opts = require('optimist').argv,
|
||||
port = opts.port || opts.p || 8000,
|
||||
dir = opts.root || opts._[0] || process.cwd();
|
||||
|
||||
if(opts.help || opts.h) {
|
||||
var u = console.error
|
||||
u('usage: ecstatic [dir] {options} --port PORT')
|
||||
u('see https://npm.im/ecstatic for more docs')
|
||||
return
|
||||
}
|
||||
|
||||
http.createServer(ecstatic(dir, opts))
|
||||
.listen(port, function () {
|
||||
console.log('ecstatic serving ' + dir + ' on port ' + port);
|
||||
});
|
||||
}
|
3
node_modules/ecstatic/lib/ecstatic/etag.js
generated
vendored
Normal file
3
node_modules/ecstatic/lib/ecstatic/etag.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = function (stat) {
|
||||
return JSON.stringify([stat.ino, stat.size, stat.mtime].join('-'));
|
||||
}
|
57
node_modules/ecstatic/lib/ecstatic/opts.js
generated
vendored
Normal file
57
node_modules/ecstatic/lib/ecstatic/opts.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// This is so you can have options aliasing and defaults in one place.
|
||||
|
||||
module.exports = function (opts) {
|
||||
|
||||
var autoIndex = true,
|
||||
showDir = false,
|
||||
cache = 'max-age=3600',
|
||||
defaultExt;
|
||||
|
||||
if (opts) {
|
||||
[
|
||||
'autoIndex',
|
||||
'autoindex'
|
||||
].some(function (k) {
|
||||
if (typeof opts[k] !== undefined && opts[k] !== null) {
|
||||
autoIndex = opts[k];
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
[
|
||||
'showDir',
|
||||
'showdir'
|
||||
].some(function (k) {
|
||||
if (typeof opts[k] !== undefined && opts[k] !== null) {
|
||||
showDir = opts[k];
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (opts.defaultExt) {
|
||||
if (opts.defaultExt === true) {
|
||||
defaultExt = 'html';
|
||||
}
|
||||
else {
|
||||
defaultExt = opts.defaultExt;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.cache !== undefined) {
|
||||
if (typeof opts.cache === 'string') {
|
||||
cache = opts.cache
|
||||
} else if(typeof opts.cache === 'number') {
|
||||
cache = 'max-age=' + opts.cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
cache: cache,
|
||||
autoIndex: autoIndex,
|
||||
showDir: showDir,
|
||||
defaultExt: defaultExt,
|
||||
baseDir: (opts && opts.baseDir) || '/'
|
||||
}
|
||||
}
|
148
node_modules/ecstatic/lib/ecstatic/showdir.js
generated
vendored
Normal file
148
node_modules/ecstatic/lib/ecstatic/showdir.js
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
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 (opts, stat) {
|
||||
var cache = opts.cache || 3600,
|
||||
root = path.resolve(opts.root),
|
||||
baseDir = opts.baseDir || '/';
|
||||
|
||||
return function middleware (req, res, next) {
|
||||
|
||||
// Figure out the path for the file from the given url
|
||||
var parsed = url.parse(req.url),
|
||||
pathname = decodeURI(parsed.pathname),
|
||||
dir = path.normalize(
|
||||
path.join(root,
|
||||
path.relative(
|
||||
path.join('/', baseDir),
|
||||
pathname
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
fs.stat(dir, function (err, stat) {
|
||||
if (err) {
|
||||
return status[500](res, next, { error: err });
|
||||
}
|
||||
|
||||
fs.readdir(dir, 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', cache);
|
||||
|
||||
sortByIsDirectory(files, function (errs, dirs, files) {
|
||||
|
||||
if (errs.length > 0) {
|
||||
return status[500](res, next, { error: errs[0] });
|
||||
}
|
||||
|
||||
// if it makes sense to, add a .. link
|
||||
if (path.resolve(dir, '..').slice(0, root.length) == root) {
|
||||
return fs.stat(path.join(dir, '..'), function (err, s) {
|
||||
if (err) {
|
||||
return status[500](res, next, { error: err });
|
||||
}
|
||||
dirs.unshift([ '..', s ]);
|
||||
render(dirs, files);
|
||||
});
|
||||
}
|
||||
render(dirs, files);
|
||||
});
|
||||
|
||||
function sortByIsDirectory(paths, cb) {
|
||||
var pending = paths.length,
|
||||
errs = [],
|
||||
dirs = [],
|
||||
files = [];
|
||||
|
||||
if (!pending) {
|
||||
return cb(errs, dirs, files);
|
||||
}
|
||||
|
||||
paths.forEach(function (file) {
|
||||
fs.stat(path.join(dir, file), function (err, s) {
|
||||
if (err) {
|
||||
errs.push(err);
|
||||
}
|
||||
else if (s.isDirectory()) {
|
||||
dirs.push([file, s]);
|
||||
}
|
||||
else {
|
||||
files.push([file, s]);
|
||||
}
|
||||
|
||||
if (--pending === 0) {
|
||||
cb(errs, dirs, files);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function render(dirs, files) {
|
||||
// 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>\n';
|
||||
|
||||
html += '<table>';
|
||||
|
||||
var writeRow = function (file, i) {
|
||||
html += '<tr><td><code>(' + perms(file[1]) + ')</code> <a href="'
|
||||
+ ent.encode(encodeURI(
|
||||
req.url.replace(/\/$/, '')
|
||||
+ '/'
|
||||
+ file[0]
|
||||
)) + '">' + ent.encode(file[0]) + '</a></td></tr>\n';
|
||||
}
|
||||
|
||||
dirs.sort(function (a, b) { return b[0] - a[0] }).forEach(writeRow);
|
||||
files.sort(function (a, b) { return b[0] - a[0] }).forEach(writeRow);
|
||||
|
||||
html += '</table>\n';
|
||||
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>\n'
|
||||
+ '</body></html>'
|
||||
;
|
||||
|
||||
res.writeHead(200, { "Content-Type": "text/html" });
|
||||
res.end(html);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function perms(stat) {
|
||||
var dir = stat.isDirectory() ? 'd' : '-',
|
||||
mode = stat.mode.toString(8);
|
||||
|
||||
return dir + mode.slice(-3).split('').map(function (n) {
|
||||
return [
|
||||
'---',
|
||||
'--x',
|
||||
'-w-',
|
||||
'-wx',
|
||||
'r--',
|
||||
'r-x',
|
||||
'rw-',
|
||||
'rwx'
|
||||
][parseInt(n, 10)]
|
||||
}).join('');
|
||||
}
|
57
node_modules/ecstatic/lib/ecstatic/status-handlers.js
generated
vendored
Normal file
57
node_modules/ecstatic/lib/ecstatic/status-handlers.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// not modified
|
||||
exports['304'] = function (res, next) {
|
||||
res.statusCode = 304;
|
||||
res.end();
|
||||
};
|
||||
|
||||
// access denied
|
||||
exports['403'] = function (res, next) {
|
||||
res.statusCode = 403;
|
||||
if (typeof next === "function") {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
if (res.writable) {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
res.end('ACCESS DENIED');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// disallowed method
|
||||
exports['405'] = function (res, next, opts) {
|
||||
res.statusCode = 405;
|
||||
if (typeof next === "function") {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
res.setHeader('allow', (opts && opts.allow) || 'GET, HEAD');
|
||||
res.end();
|
||||
}
|
||||
};
|
||||
|
||||
// not found
|
||||
exports['404'] = function (res, next) {
|
||||
res.statusCode = 404;
|
||||
if (typeof next === "function") {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
if (res.writable) {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
res.end('File not found. :(');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// flagrant error
|
||||
exports['500'] = function (res, next, opts) {
|
||||
res.statusCode = 500;
|
||||
res.end(opts.error.stack || opts.error.toString() || "No specified error");
|
||||
};
|
||||
|
||||
// bad request
|
||||
exports['400'] = function (res, next, opts) {
|
||||
res.statusCode = 400;
|
||||
res.end(opts && opts.error ? String(opts.error) : 'Malformed request.');
|
||||
};
|
Reference in New Issue
Block a user