Skip to content

Instantly share code, notes, and snippets.

@tleunen
Last active December 12, 2015 02:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tleunen/4702769 to your computer and use it in GitHub Desktop.
Save tleunen/4702769 to your computer and use it in GitHub Desktop.
Simple static files webserver in NodeJS (with SSL in comments)
var http = require('http'),
https = require('https'),
fs = require('fs'),
url = require('url');
/*
var options = {
key: fs.readFileSync('ssl/server.key'),
cert: fs.readFileSync('ssl/server.crt')
};
*/
var mimeTypes = {
"txt": "text/plain",
"html": "text/html",
"css": "text/css",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"png": "image/png",
"js": "application/javascript",
"json": "application/json",
"xml": "application/xml"
};
var dirSpacesBeforeDate = 51;
var dirSpacesBeforeSize = 9;
var dirMonths = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(',');
http.createServer(function(req, res) {
serveFiles(req, res);
}).listen(80);
/*
https.createServer(options, function(req, res) {
serveFiles(req, res);
}).listen(443);
*/
function serveFiles(req, res) {
var path = url.parse(req.url).pathname;
path = ('./' + path).replace('//', '/');
// prevent access to file starting with .
var parts = path.split('/');
if(parts[parts.length-1].charAt(0) === '.')
return sendForbidden(req, res, path);
fs.stat(path, function(err, stats) {
if(err) return sendNotFound(req, res, path);
if(stats.isDirectory()) {
if(path.charAt(path.length-1) !== '/') {
return sendRedirect(req, res, path + '/');
}
fs.stat(path + 'index.html', function(err2, stats2) {
if(err2) return sendDirectory(req, res, path);
return sendFile(req, res, path + '/index.html');
});
}
else
return sendFile(req, res, path);
});
}
function escapeHtml(value) {
return value.toString().
replace('<', '&lt;').
replace('>', '&gt;').
replace('"', '&quot;');
}
function zeroFill(value) {
return ((value < 10) ? '0' : '') + value;
}
function convertSize(value) {
if(value > 1000000000) return ((value*0.000000001) | 0) + 'G';
if(value > 1000000) return ((value*0.000001) | 0) + 'M';
if(value > 10000) return ((value*0.001) | 0) + 'K';
return '' + value;
}
function sendFile(req, res, path) {
var extension = path.split('.').pop();
var contentType = mimeTypes[extension] || 'text/plain';
res.writeHead(200, {'Content-Type': contentType});
var fileStream = fs.createReadStream(path);
fileStream.pipe(res);
}
function sendRedirect(req, res, path) {
res.writeHead(301, {
'Content-Type': 'text/html',
'Location': path
});
res.end();
}
function sendServerError(req, res, error) {
console.log('500 Internal Server Error: ' + error);
res.writeHead(500, {'Content-Type': 'text/html'});
res.writeo('<!DOCTYPE html>\n');
res.write('<html><head>\n');
res.write('<title>500 Internal Server Error</title>\n');
res.write('</head><body>\n');
res.write('<h1>500 Internal Server Error</h1>\n');
res.write('<pre>' + escapeHtml(error) + '</pre>\n');
res.write('</body></html>\n');
res.end();
}
function sendForbidden(req, res, path) {
console.log('403 Forbidden: ' + path);
res.writeHead(403, {'Content-Type': 'text/html'});
res.write('<!DOCTYPE html>\n');
res.write('<html><head>\n');
res.write('<title>403 Forbidden</title>\n');
res.write('</head><body>\n');
res.write('<h1>403 Forbidden</h1>\n');
res.write('<p>You don\'t have permission to access' + escapeHtml(path) + ' on this server.</p>\n');
res.write('</body></html>\n');
res.end();
}
function sendNotFound(req, res, path) {
console.log('404 Not Found: ' + path);
res.writeHead(404, {'Content-Type': 'text/html'});
res.write('<!DOCTYPE html>\n');
res.write('<html><head>\n');
res.write('<title>404 Not Found</title>\n');
res.write('</head><body>\n');
res.write('<h1>404 Not Found</h1>\n');
res.write('<p>The requested URL ' + escapeHtml(path) + ' was not found on this server.\n');
res.write('</body></html>\n');
res.end();
}
function sendDirectory(req, res, path) {
fs.readdir(path, function(err, files) {
if(err) return sendServerError(req, res, err);
if(files.length === 0)
return sendDirectoryIndex(req, res, path, []);
var remaining = files.length;
files.forEach(function(filename, idx) {
fs.stat(path + '/' + filename, function(err, stats) {
if(err) return sendServerError(req, res, err);
files[idx] = {
name: files[idx],
date: stats.mtime,
size: '-'
};
if(stats.isDirectory()) files[idx].name += '/';
else files[idx].size = stats.size;
if(--remaining === 0)
return sendDirectoryIndex(req, res, path, files);
});
});
});
}
function sendDirectoryIndex(req, res, path, files) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<!DOCTYPE html>\n');
res.write('<html><head>\n');
res.write('<title>Index of ' + escapeHtml(path) + '</title>\n');
res.write('</head><body>\n');
res.write('<h1>Index of ' + escapeHtml(path) + '</h1>\n');
res.write('<hr><pre>\n');
res.write('<a href="../">../</a>\n');
files.forEach(function(file, idx) {
var name = escapeHtml(file.name),
displayName = name.substr(0, dirSpacesBeforeDate-1),
spBeforeDate = dirSpacesBeforeDate - displayName.length;
res.write('<a href="' + name + '">' + displayName + '</a>');
while(--spBeforeDate) res.write(' ');
var day = zeroFill(file.date.getDate()),
month = dirMonths[file.date.getMonth()],
hours = zeroFill(file.date.getHours()),
min = zeroFill(file.date.getMinutes());
var date = day + '-' + month + '-' + file.date.getFullYear() +
' ' + hours + ':' + min;
res.write(date);
var size = convertSize(file.size),
spBeforeSize = dirSpacesBeforeSize - size.length;
while(spBeforeSize--) res.write(' ');
res.write(size + '\n');
});
res.write('</pre><hr></body></html>\n');
res.end();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment