Update ecstatic to 0.5 (resolve #800)
This commit is contained in:
229
templates/bin/node/http-server/node_modules/ecstatic/lib/ecstatic.js
generated
vendored
229
templates/bin/node/http-server/node_modules/ecstatic/lib/ecstatic.js
generated
vendored
@@ -27,6 +27,7 @@ var ecstatic = module.exports = function (dir, options) {
|
||||
handleError = opts.handleError;
|
||||
|
||||
opts.root = dir;
|
||||
if (defaultExt && /^\./.test(defaultExt)) defaultExt = defaultExt.replace(/^\./, '');
|
||||
|
||||
return function middleware (req, res, next) {
|
||||
|
||||
@@ -37,8 +38,8 @@ var ecstatic = module.exports = function (dir, options) {
|
||||
// Figure out the path for the file from the given url
|
||||
var parsed = url.parse(req.url);
|
||||
try {
|
||||
decodeURI(req.url); // check validity of url
|
||||
var pathname = decodeURI(parsed.pathname);
|
||||
decodeURIComponent(req.url); // check validity of url
|
||||
var pathname = decodePathname(parsed.pathname);
|
||||
}
|
||||
catch (err) {
|
||||
return status[400](res, next, { error: err });
|
||||
@@ -67,80 +68,136 @@ var ecstatic = module.exports = function (dir, options) {
|
||||
return status[405](res, next);
|
||||
}
|
||||
|
||||
function statFile() {
|
||||
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(parsed.pathname).length) {
|
||||
//
|
||||
// If no file extension is specified and there is a default extension
|
||||
// try that before rendering 404.html.
|
||||
//
|
||||
middleware({
|
||||
url: parsed.pathname + '.' + defaultExt + ((parsed.search)? parsed.search:'')
|
||||
}, res, next);
|
||||
}
|
||||
else {
|
||||
// Try for ./404.html
|
||||
//
|
||||
// In order to make tests pass, we have to punch the status code
|
||||
// in both spots. It's stupid and mysterious, but at least we get
|
||||
// the behavior we want.
|
||||
//
|
||||
// TODO: Figure out what the Hell is going on and clean this up.
|
||||
res.statusCode = 404;
|
||||
middleware({
|
||||
url: (handleError ? ('/' + path.join(baseDir, '404.' + defaultExt)) : req.url),
|
||||
statusCode: 404
|
||||
}, res, next);
|
||||
}
|
||||
}
|
||||
else if (err) {
|
||||
status[500](res, next, { error: err });
|
||||
}
|
||||
else if (stat.isDirectory()) {
|
||||
// 302 to / if necessary
|
||||
if (!parsed.pathname.match(/\/$/)) {
|
||||
res.statusCode = 302;
|
||||
res.setHeader('location', parsed.pathname + '/' +
|
||||
(parsed.query? ('?' + parsed.query):'')
|
||||
);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
if (autoIndex) {
|
||||
return middleware({
|
||||
url: path.join(encodeURIComponent(pathname), '/index.' + defaultExt)
|
||||
}, 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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);
|
||||
} else {
|
||||
statFile();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
statFile();
|
||||
}
|
||||
|
||||
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(parsed.pathname).length) {
|
||||
//
|
||||
// If no file extension is specified and there is a default extension
|
||||
// try that before rendering 404.html.
|
||||
//
|
||||
middleware({
|
||||
url: parsed.pathname + '.' + defaultExt + ((parsed.search)? parsed.search:'')
|
||||
}, res, next);
|
||||
}
|
||||
else {
|
||||
// Try for ./404.html
|
||||
middleware({
|
||||
url: (handleError ? ('/' + path.join(baseDir, '404.html')) : req.url),
|
||||
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 (!parsed.pathname.match(/\/$/)) {
|
||||
res.statusCode = 302;
|
||||
res.setHeader('location', parsed.pathname + '/' +
|
||||
(parsed.query? ('?' + parsed.query):'')
|
||||
);
|
||||
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) {
|
||||
// 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, 'utf-8');
|
||||
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"));
|
||||
}
|
||||
|
||||
var range = (req.headers && req.headers['range']);
|
||||
if (range) {
|
||||
var total = stat.size;
|
||||
var parts = range.replace(/bytes=/, "").split("-");
|
||||
var partialstart = parts[0];
|
||||
var partialend = parts[1];
|
||||
var start = parseInt(partialstart, 10);
|
||||
var end = Math.min(total-1, partialend ? parseInt(partialend, 10) : total-1);
|
||||
var chunksize = (end-start)+1;
|
||||
if (start > end || isNaN(start) || isNaN(end)) {
|
||||
return status['416'](res, next);
|
||||
}
|
||||
var fstream = fs.createReadStream(file, {start: start, end: end});
|
||||
fstream.on('error', function (err) {
|
||||
status['500'](res, next, { error: err });
|
||||
});
|
||||
res.writeHead(206, {
|
||||
'Content-Range': 'bytes ' + start + '-' + end + '/' + total,
|
||||
'Accept-Ranges': 'bytes',
|
||||
'Content-Length': chunksize,
|
||||
'Content-Type': contentType || 'application/octet-stream'
|
||||
});
|
||||
fstream.pipe(res);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Helper for this, with default headers.
|
||||
res.setHeader('etag', etag(stat));
|
||||
@@ -158,25 +215,6 @@ var ecstatic = module.exports = function (dir, options) {
|
||||
}
|
||||
|
||||
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") {
|
||||
@@ -215,13 +253,28 @@ function shouldCompress(req) {
|
||||
;
|
||||
}
|
||||
|
||||
if(!module.parent) {
|
||||
// See: https://github.com/jesusabdullah/node-ecstatic/issues/109
|
||||
function decodePathname(pathname) {
|
||||
var pieces = pathname.split('/');
|
||||
|
||||
return pieces.map(function (piece) {
|
||||
piece = decodeURIComponent(piece);
|
||||
|
||||
if (process.platform === 'win32' && /\\/.test(piece)) {
|
||||
throw new Error('Invalid forward slash character');
|
||||
}
|
||||
|
||||
return piece;
|
||||
}).join('/');
|
||||
}
|
||||
|
||||
if (!module.parent) {
|
||||
var http = require('http'),
|
||||
opts = require('optimist').argv,
|
||||
opts = require('minimist')(process.argv.slice(2)),
|
||||
port = opts.port || opts.p || 8000,
|
||||
dir = opts.root || opts._[0] || process.cwd();
|
||||
|
||||
if(opts.help || opts.h) {
|
||||
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');
|
||||
@@ -230,6 +283,6 @@ if(!module.parent) {
|
||||
|
||||
http.createServer(ecstatic(dir, opts))
|
||||
.listen(port, function () {
|
||||
console.log('ecstatic serving ' + dir + ' on port ' + port);
|
||||
console.log('ecstatic serving ' + dir + ' at http://0.0.0.0:' + port);
|
||||
});
|
||||
}
|
||||
|
||||
13
templates/bin/node/http-server/node_modules/ecstatic/lib/ecstatic/opts.js
generated
vendored
13
templates/bin/node/http-server/node_modules/ecstatic/lib/ecstatic/opts.js
generated
vendored
@@ -3,12 +3,12 @@
|
||||
module.exports = function (opts) {
|
||||
|
||||
var autoIndex = true,
|
||||
showDir = false,
|
||||
showDir = true,
|
||||
humanReadable = true,
|
||||
si = false,
|
||||
cache = 'max-age=3600',
|
||||
gzip = false,
|
||||
defaultExt,
|
||||
defaultExt = '.html',
|
||||
handleError = true;
|
||||
|
||||
if (opts) {
|
||||
@@ -53,13 +53,8 @@ module.exports = function (opts) {
|
||||
}
|
||||
});
|
||||
|
||||
if (opts.defaultExt) {
|
||||
if (opts.defaultExt === true) {
|
||||
defaultExt = 'html';
|
||||
}
|
||||
else {
|
||||
defaultExt = opts.defaultExt;
|
||||
}
|
||||
if (opts.defaultExt && typeof opts.defaultExt === 'string') {
|
||||
defaultExt = opts.defaultExt;
|
||||
}
|
||||
|
||||
if (typeof opts.cache !== 'undefined' && opts.cache !== null) {
|
||||
|
||||
69
templates/bin/node/http-server/node_modules/ecstatic/lib/ecstatic/showdir.js
generated
vendored
69
templates/bin/node/http-server/node_modules/ecstatic/lib/ecstatic/showdir.js
generated
vendored
@@ -1,7 +1,7 @@
|
||||
var ecstatic = require('../ecstatic'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
ent = require('ent'),
|
||||
he = require('he'),
|
||||
etag = require('./etag'),
|
||||
url = require('url'),
|
||||
status = require('./status-handlers');
|
||||
@@ -18,7 +18,7 @@ module.exports = function (opts, stat) {
|
||||
|
||||
// Figure out the path for the file from the given url
|
||||
var parsed = url.parse(req.url),
|
||||
pathname = decodeURI(parsed.pathname),
|
||||
pathname = decodeURIComponent(parsed.pathname),
|
||||
dir = path.normalize(
|
||||
path.join(root,
|
||||
path.relative(
|
||||
@@ -43,11 +43,14 @@ module.exports = function (opts, 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] });
|
||||
}
|
||||
sortByIsDirectory(files, function (lolwuts, dirs, files) {
|
||||
// It's possible to get stat errors for all sorts of reasons here.
|
||||
// Unfortunately, our two choices are to either bail completely,
|
||||
// or just truck along as though everything's cool. In this case,
|
||||
// I decided to just tack them on as "??!?" items along with dirs
|
||||
// and files.
|
||||
//
|
||||
// Whatever.
|
||||
|
||||
// if it makes sense to, add a .. link
|
||||
if (path.resolve(dir, '..').slice(0, root.length) == root) {
|
||||
@@ -56,10 +59,10 @@ module.exports = function (opts, stat) {
|
||||
return status[500](res, next, { error: err });
|
||||
}
|
||||
dirs.unshift([ '..', s ]);
|
||||
render(dirs, files);
|
||||
render(dirs, files, lolwuts);
|
||||
});
|
||||
}
|
||||
render(dirs, files);
|
||||
render(dirs, files, lolwuts);
|
||||
});
|
||||
|
||||
function sortByIsDirectory(paths, cb) {
|
||||
@@ -78,7 +81,7 @@ module.exports = function (opts, stat) {
|
||||
paths.forEach(function (file) {
|
||||
fs.stat(path.join(dir, file), function (err, s) {
|
||||
if (err) {
|
||||
errs.push(err);
|
||||
errs.push([file, err]);
|
||||
}
|
||||
else if (s.isDirectory()) {
|
||||
dirs.push([file, s]);
|
||||
@@ -94,25 +97,27 @@ module.exports = function (opts, stat) {
|
||||
});
|
||||
}
|
||||
|
||||
function render(dirs, files) {
|
||||
function render(dirs, files, lolwuts) {
|
||||
// each entry in the array is a [name, stat] tuple
|
||||
|
||||
// TODO: use stylessheets?
|
||||
var html = '<!doctype html>\
|
||||
<html> \
|
||||
<head> \
|
||||
<meta charset="utf-8"> \
|
||||
<title>Index of ' + pathname +'</title> \
|
||||
</head> \
|
||||
<body> \
|
||||
<h1>Index of ' + pathname + '</h1>\n';
|
||||
var html = [
|
||||
'<!doctype html>',
|
||||
'<html>',
|
||||
' <head>',
|
||||
' <meta charset="utf-8">',
|
||||
' <title>Index of ' + pathname +'</title>',
|
||||
' </head>',
|
||||
' <body>',
|
||||
'<h1>Index of ' + pathname + '</h1>'
|
||||
].join('\n') + '\n';
|
||||
|
||||
html += '<table>';
|
||||
|
||||
var failed = false;
|
||||
var writeRow = function (file, i) {
|
||||
// render a row given a [name, stat] tuple
|
||||
var isDir = file[1].isDirectory();
|
||||
var isDir = file[1].isDirectory && file[1].isDirectory();
|
||||
var href =
|
||||
parsed.pathname.replace(/\/$/, '') +
|
||||
'/' + encodeURIComponent(file[0]);
|
||||
@@ -122,7 +127,7 @@ module.exports = function (opts, stat) {
|
||||
href += '/' + ((parsed.search)? parsed.search:'');
|
||||
}
|
||||
|
||||
var displayName = ent.encode(file[0]) + ((isDir)? '/':'');
|
||||
var displayName = he.encode(file[0]) + ((isDir)? '/':'');
|
||||
|
||||
// TODO: use stylessheets?
|
||||
html += '<tr>' +
|
||||
@@ -132,15 +137,16 @@ module.exports = function (opts, stat) {
|
||||
'</tr>\n';
|
||||
};
|
||||
|
||||
dirs.sort(function (a, b) { return b[0] - a[0]; } ).forEach(writeRow);
|
||||
dirs.sort(function (a, b) { return b[0] - a[0]; }).forEach(writeRow);
|
||||
files.sort(function (a, b) { return b.toString().localeCompare(a.toString()); }).forEach(writeRow);
|
||||
lolwuts.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' +
|
||||
he.encode(req.headers.host || '') + '</address>\n' +
|
||||
'</body></html>'
|
||||
;
|
||||
|
||||
@@ -155,6 +161,11 @@ module.exports = function (opts, stat) {
|
||||
};
|
||||
|
||||
function permsToString(stat) {
|
||||
|
||||
if (!stat.isDirectory || !stat.mode) {
|
||||
return '???!!!???';
|
||||
}
|
||||
|
||||
var dir = stat.isDirectory() ? 'd' : '-',
|
||||
mode = stat.mode.toString(8);
|
||||
|
||||
@@ -177,7 +188,7 @@ function permsToString(stat) {
|
||||
// si: (boolean) whether to use si (1k = 1000), otherwise 1k = 1024
|
||||
// adopted from http://stackoverflow.com/a/14919494/665507
|
||||
function sizeToString(stat, humanReadable, si) {
|
||||
if (stat.isDirectory()) {
|
||||
if (stat.isDirectory && stat.isDirectory()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -185,15 +196,19 @@ function sizeToString(stat, humanReadable, si) {
|
||||
var bytes = stat.size;
|
||||
var threshold = si ? 1000 : 1024;
|
||||
|
||||
if(!humanReadable || bytes < threshold) {
|
||||
if (!humanReadable || bytes < threshold) {
|
||||
return bytes + 'B';
|
||||
}
|
||||
|
||||
var units = ['k','M','G','T','P','E','Z','Y'];
|
||||
var units = [ 'k','M','G','T','P','E','Z','Y' ];
|
||||
var u = -1;
|
||||
do {
|
||||
bytes /= threshold;
|
||||
++u;
|
||||
} while (bytes >= threshold);
|
||||
return bytes.toFixed(1)+units[u];
|
||||
|
||||
var b = bytes.toFixed(1);
|
||||
if (isNaN(b)) b = '??';
|
||||
|
||||
return b + units[u];
|
||||
}
|
||||
|
||||
15
templates/bin/node/http-server/node_modules/ecstatic/lib/ecstatic/status-handlers.js
generated
vendored
15
templates/bin/node/http-server/node_modules/ecstatic/lib/ecstatic/status-handlers.js
generated
vendored
@@ -26,7 +26,7 @@ exports['405'] = function (res, next, opts) {
|
||||
}
|
||||
else {
|
||||
res.setHeader('allow', (opts && opts.allow) || 'GET, HEAD');
|
||||
res.end();
|
||||
res.end();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,6 +44,19 @@ exports['404'] = function (res, next) {
|
||||
}
|
||||
};
|
||||
|
||||
exports['416'] = function (res, next) {
|
||||
res.statusCode = 416;
|
||||
if (typeof next === "function") {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
if (res.writable) {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
res.end('Requested range not satisfiable');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// flagrant error
|
||||
exports['500'] = function (res, next, opts) {
|
||||
res.statusCode = 500;
|
||||
|
||||
Reference in New Issue
Block a user