Mercurial > hg > nodescore
diff node_modules/node-static/lib/node-static.js @ 69:333afcfd3f3a
added node_modules to project and fixed path to chronometer
also added deps to installer script
author | tzara <rc-web@kiben.net> |
---|---|
date | Sat, 26 Oct 2013 14:12:50 +0100 |
parents | |
children | 0ae87af84e2f |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/node_modules/node-static/lib/node-static.js Sat Oct 26 14:12:50 2013 +0100 @@ -0,0 +1,323 @@ +var fs = require('fs') + , events = require('events') + , buffer = require('buffer') + , http = require('http') + , url = require('url') + , path = require('path') + , mime = require('mime') + , util = require('./node-static/util'); + +// Current version +var version = [0, 7, 2]; + +Server = function (root, options) { + if (root && (typeof(root) === 'object')) { options = root; root = null } + + this.root = path.resolve(root || '.'); + this.options = options || {}; + this.cache = 3600; + + this.defaultHeaders = {}; + this.options.headers = this.options.headers || {}; + + if ('cache' in this.options) { + if (typeof(this.options.cache) === 'number') { + this.cache = this.options.cache; + } else if (! this.options.cache) { + this.cache = false; + } + } + + if ('serverInfo' in this.options) { + this.serverInfo = this.options.serverInfo.toString(); + } else { + this.serverInfo = 'node-static/' + version.join('.'); + } + + this.defaultHeaders['server'] = this.serverInfo; + + if (this.cache !== false) { + this.defaultHeaders['cache-control'] = 'max-age=' + this.cache; + } + + for (var k in this.defaultHeaders) { + this.options.headers[k] = this.options.headers[k] || + this.defaultHeaders[k]; + } +}; + +Server.prototype.serveDir = function (pathname, req, res, finish) { + var htmlIndex = path.join(pathname, 'index.html'), + that = this; + + fs.stat(htmlIndex, function (e, stat) { + if (!e) { + var status = 200; + var headers = {}; + var originalPathname = decodeURI(url.parse(req.url).pathname); + if (originalPathname.length && originalPathname.charAt(originalPathname.length - 1) !== '/') { + return finish(301, { 'Location': originalPathname + '/' }); + } else { + that.respond(null, status, headers, [htmlIndex], stat, req, res, finish); + } + } else { + // Stream a directory of files as a single file. + fs.readFile(path.join(pathname, 'index.json'), function (e, contents) { + if (e) { return finish(404, {}) } + var index = JSON.parse(contents); + streamFiles(index.files); + }); + } + }); + function streamFiles(files) { + util.mstat(pathname, files, function (e, stat) { + if (e) { return finish(404, {}) } + that.respond(pathname, 200, {}, files, stat, req, res, finish); + }); + } +}; + +Server.prototype.serveFile = function (pathname, status, headers, req, res) { + var that = this; + var promise = new(events.EventEmitter); + + pathname = this.resolve(pathname); + + fs.stat(pathname, function (e, stat) { + if (e) { + return promise.emit('error', e); + } + that.respond(null, status, headers, [pathname], stat, req, res, function (status, headers) { + that.finish(status, headers, req, res, promise); + }); + }); + return promise; +}; + +Server.prototype.finish = function (status, headers, req, res, promise, callback) { + var result = { + status: status, + headers: headers, + message: http.STATUS_CODES[status] + }; + + headers['server'] = this.serverInfo; + + if (!status || status >= 400) { + if (callback) { + callback(result); + } else { + if (promise.listeners('error').length > 0) { + promise.emit('error', result); + } + else { + res.writeHead(status, headers); + res.end(); + } + } + } else { + // Don't end the request here, if we're streaming; + // it's taken care of in `prototype.stream`. + if (status !== 200 || req.method !== 'GET') { + res.writeHead(status, headers); + res.end(); + } + callback && callback(null, result); + promise.emit('success', result); + } +}; + +Server.prototype.servePath = function (pathname, status, headers, req, res, finish) { + var that = this, + promise = new(events.EventEmitter); + + pathname = this.resolve(pathname); + + // Make sure we're not trying to access a + // file outside of the root. + if (pathname.indexOf(that.root) === 0) { + fs.stat(pathname, function (e, stat) { + if (e) { + finish(404, {}); + } else if (stat.isFile()) { // Stream a single file. + that.respond(null, status, headers, [pathname], stat, req, res, finish); + } else if (stat.isDirectory()) { // Stream a directory of files. + that.serveDir(pathname, req, res, finish); + } else { + finish(400, {}); + } + }); + } else { + // Forbidden + finish(403, {}); + } + return promise; +}; + +Server.prototype.resolve = function (pathname) { + return path.resolve(path.join(this.root, pathname)); +}; + +Server.prototype.serve = function (req, res, callback) { + var that = this, + promise = new(events.EventEmitter), + pathname; + + var finish = function (status, headers) { + that.finish(status, headers, req, res, promise, callback); + }; + + try { + pathname = decodeURI(url.parse(req.url).pathname); + } + catch(e) { + return process.nextTick(function() { + return finish(400, {}); + }); + } + + process.nextTick(function () { + that.servePath(pathname, 200, {}, req, res, finish).on('success', function (result) { + promise.emit('success', result); + }).on('error', function (err) { + promise.emit('error'); + }); + }); + if (! callback) { return promise } +}; + +/* Check if we should consider sending a gzip version of the file based on the + * file content type and client's Accept-Encoding header value. + */ +Server.prototype.gzipOk = function(req, contentType) { + var enable = this.options.gzip; + if(enable && + (typeof enable === 'boolean' || + (contentType && (enable instanceof RegExp) && enable.test(contentType)))) { + var acceptEncoding = req.headers['accept-encoding']; + return acceptEncoding && acceptEncoding.indexOf("gzip") >= 0; + } + return false; +} + +/* Send a gzipped version of the file if the options and the client indicate gzip is enabled and + * we find a .gz file mathing the static resource requested. + */ +Server.prototype.respondGzip = function(pathname, status, contentType, _headers, files, stat, req, res, finish) { + var that = this; + if(files.length == 1 && this.gzipOk(req, contentType)) { + var gzFile = files[0] + ".gz"; + fs.stat(gzFile, function(e, gzStat) { + if(!e && gzStat.isFile()) { + //console.log('Serving', gzFile, 'to gzip-capable client instead of', files[0], 'new size is', gzStat.size, 'uncompressed size', stat.size); + var vary = _headers['Vary']; + _headers['Vary'] = (vary && vary != 'Accept-Encoding'?vary+', ':'')+'Accept-Encoding'; + _headers['Content-Encoding'] = 'gzip'; + stat.size = gzStat.size; + files = [gzFile]; + } else { + //console.log('gzip file not found or error finding it', gzFile, String(e), stat.isFile()); + } + that.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish); + }); + } else { + // Client doesn't want gzip or we're sending multiple files + that.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish); + } +} + +Server.prototype.respondNoGzip = function (pathname, status, contentType, _headers, files, stat, req, res, finish) { + var mtime = Date.parse(stat.mtime), + key = pathname || files[0], + headers = {}, + clientETag = req.headers['if-none-match'], + clientMTime = Date.parse(req.headers['if-modified-since']); + + + // Copy default headers + for (var k in this.options.headers) { headers[k] = this.options.headers[k] } + // Copy custom headers + for (var k in _headers) { headers[k] = _headers[k] } + + headers['Etag'] = JSON.stringify([stat.ino, stat.size, mtime].join('-')); + headers['Date'] = new(Date)().toUTCString(); + headers['Last-Modified'] = new(Date)(stat.mtime).toUTCString(); + headers['Content-Type'] = contentType; + headers['Content-Length'] = stat.size; + + for (var k in _headers) { headers[k] = _headers[k] } + + // Conditional GET + // If the "If-Modified-Since" or "If-None-Match" headers + // match the conditions, send a 304 Not Modified. + if ((clientMTime || clientETag) && + (!clientETag || clientETag === headers['Etag']) && + (!clientMTime || clientMTime >= mtime)) { + // 304 response should not contain entity headers + ['Content-Encoding', + 'Content-Language', + 'Content-Length', + 'Content-Location', + 'Content-MD5', + 'Content-Range', + 'Content-Type', + 'Expires', + 'Last-Modified'].forEach(function(entityHeader) { + delete headers[entityHeader]; + }); + finish(304, headers); + } else { + res.writeHead(status, headers); + + this.stream(pathname, files, new(buffer.Buffer)(stat.size), res, function (e, buffer) { + if (e) { return finish(500, {}) } + finish(status, headers); + }); + } +}; + +Server.prototype.respond = function (pathname, status, _headers, files, stat, req, res, finish) { + var contentType = _headers['Content-Type'] || + mime.lookup(files[0]) || + 'application/octet-stream'; + if(this.options.gzip) { + this.respondGzip(pathname, status, contentType, _headers, files, stat, req, res, finish); + } else { + this.respondNoGzip(pathname, status, contentType, _headers, files, stat, req, res, finish); + } +} + +Server.prototype.stream = function (pathname, files, buffer, res, callback) { + (function streamFile(files, offset) { + var file = files.shift(); + + if (file) { + file = file[0] === '/' ? file : path.join(pathname || '.', file); + + // Stream the file to the client + fs.createReadStream(file, { + flags: 'r', + mode: 0666 + }).on('data', function (chunk) { + chunk.copy(buffer, offset); + offset += chunk.length; + }).on('close', function () { + streamFile(files, offset); + }).on('error', function (err) { + callback(err); + console.error(err); + }).pipe(res, { end: false }); + } else { + res.end(); + callback(null, buffer, offset); + } + })(files.slice(0), 0); +}; + +// Exports +exports.Server = Server; +exports.version = version; +exports.mime = mime; + + +