From e4f45060b1e4184a59b9257605078cc235ae8b5f Mon Sep 17 00:00:00 2001 From: Jack Allnutt Date: Tue, 19 Jul 2011 02:56:58 +0100 Subject: [PATCH] npm install node-static --- node/node_modules/node-static/LICENSE | 20 ++ node/node_modules/node-static/README.md | 141 ++++++++++ .../benchmark/node-static-0.3.0.txt | 43 +++ .../node-static/examples/file-server.js | 26 ++ .../node-static/lib/node-static.js | 252 ++++++++++++++++++ .../node-static/lib/node-static/mime.js | 139 ++++++++++ .../node-static/lib/node-static/util.js | 30 +++ node/node_modules/node-static/package.json | 15 ++ 8 files changed, 666 insertions(+) create mode 100644 node/node_modules/node-static/LICENSE create mode 100644 node/node_modules/node-static/README.md create mode 100644 node/node_modules/node-static/benchmark/node-static-0.3.0.txt create mode 100644 node/node_modules/node-static/examples/file-server.js create mode 100644 node/node_modules/node-static/lib/node-static.js create mode 100644 node/node_modules/node-static/lib/node-static/mime.js create mode 100644 node/node_modules/node-static/lib/node-static/util.js create mode 100644 node/node_modules/node-static/package.json diff --git a/node/node_modules/node-static/LICENSE b/node/node_modules/node-static/LICENSE new file mode 100644 index 0000000..91f6632 --- /dev/null +++ b/node/node_modules/node-static/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2010 Alexis Sellier + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node/node_modules/node-static/README.md b/node/node_modules/node-static/README.md new file mode 100644 index 0000000..825c811 --- /dev/null +++ b/node/node_modules/node-static/README.md @@ -0,0 +1,141 @@ +node-static +=========== + +> a simple, *rfc 2616 compliant* file streaming module for [node](http://nodejs.org) + +node-static has an in-memory file cache, making it highly efficient. +node-static understands and supports *conditional GET* and *HEAD* requests. +node-static was inspired by some of the other static-file serving modules out there, +such as node-paperboy and antinode. + +synopsis +-------- + + var static = require('node-static'); + + // + // Create a node-static server instance to serve the './public' folder + // + var file = new(static.Server)('./public'); + + require('http').createServer(function (request, response) { + request.addListener('end', function () { + // + // Serve files! + // + file.serve(request, response); + }); + }).listen(8080); + +API +--- + +### Creating a node-static Server # + +Creating a file server instance is as simple as: + + new static.Server(); + +This will serve files in the current directory. If you want to serve files in a specific +directory, pass it as the first argument: + + new static.Server('./public'); + +You can also specify how long the client is supposed to cache the files node-static serves: + + new static.Server('./public', { cache: 3600 }); + +This will set the `Cache-Control` header, telling clients to cache the file for an hour. +This is the default setting. + +### Serving files under a directory # + +To serve files under a directory, simply call the `serve` method on a `Server` instance, passing it +the HTTP request and response object: + + var fileServer = new static.Server('./public'); + + require('http').createServer(function (request, response) { + request.addListener('end', function () { + fileServer.serve(request, response); + }); + }).listen(8080); + +### Serving specific files # + +If you want to serve a specific file, like an error page for example, use the `serveFile` method: + + fileServer.serveFile('/error.html', 500, {}, request, response); + +This will serve the `error.html` file, from under the file root directory, with a `500` status code. +For example, you could serve an error page, when the initial request wasn't found: + + require('http').createServer(function (request, response) { + request.addListener('end', function () { + fileServer.serve(request, response, function (e, res) { + if (e && (e.status === 404)) { // If the file wasn't found + fileServer.serveFile('/not-found.html', request, response); + } + }); + }); + }).listen(8080); + +More on intercepting errors bellow. + +### Intercepting errors & Listening # + +An optional callback can be passed as last argument, it will be called every time a file +has been served successfully, or if there was an error serving the file: + + var fileServer = new static.Server('./public'); + + require('http').createServer(function (request, response) { + request.addListener('end', function () { + fileServer.serve(request, response, function (err, result) { + if (err) { // There was an error serving the file + sys.error("Error serving " + request.url + " - " + err.message); + + // Respond to the client + response.writeHead(err.status, err.headers); + response.end(); + } + }); + }); + }).listen(8080); + +Note that if you pass a callback, and there is an error serving the file, node-static +*will not* respond to the client. This gives you the opportunity to re-route the request, +or handle it differently. + +For example, you may want to interpret a request as a static request, but if the file isn't found, +send it to an application. + +If you only want to *listen* for errors, you can use *event listeners*: + + fileServer.serve(request, response).addListener('error', function (err) { + sys.error("Error serving " + request.url + " - " + err.message); + }); + +With this method, you don't have to explicitly send the response back, in case of an error. + +### Options when creating an instance of `Server` # + +#### `cache` # + +Sets the `Cache-Control` header. + +example: `{ cache: 7200 }` + +Passing a number will set the cache duration to that number of seconds. +Passing `false` will disable the `Cache-Control` header. + +> Defaults to `3600` + +#### `headers` # + +Sets response headers. + +example: `{ 'X-Hello': 'World!' }` + +> defaults to `{}` + diff --git a/node/node_modules/node-static/benchmark/node-static-0.3.0.txt b/node/node_modules/node-static/benchmark/node-static-0.3.0.txt new file mode 100644 index 0000000..c6083ea --- /dev/null +++ b/node/node_modules/node-static/benchmark/node-static-0.3.0.txt @@ -0,0 +1,43 @@ +This is ApacheBench, Version 2.3 <$Revision: 655654 $> +Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ +Licensed to The Apache Software Foundation, http://www.apache.org/ + +Benchmarking 127.0.0.1 (be patient) + + +Server Software: node-static/0.3.0 +Server Hostname: 127.0.0.1 +Server Port: 8080 + +Document Path: /lib/node-static.js +Document Length: 6038 bytes + +Concurrency Level: 20 +Time taken for tests: 2.323 seconds +Complete requests: 10000 +Failed requests: 0 +Write errors: 0 +Total transferred: 63190000 bytes +HTML transferred: 60380000 bytes +Requests per second: 4304.67 [#/sec] (mean) +Time per request: 4.646 [ms] (mean) +Time per request: 0.232 [ms] (mean, across all concurrent requests) +Transfer rate: 26563.66 [Kbytes/sec] received + +Connection Times (ms) + min mean[+/-sd] median max +Connect: 0 0 0.2 0 3 +Processing: 1 4 1.4 4 28 +Waiting: 1 4 1.3 4 18 +Total: 2 5 1.5 4 28 + +Percentage of the requests served within a certain time (ms) + 50% 4 + 66% 5 + 75% 5 + 80% 5 + 90% 5 + 95% 6 + 98% 8 + 99% 9 + 100% 28 (longest request) diff --git a/node/node_modules/node-static/examples/file-server.js b/node/node_modules/node-static/examples/file-server.js new file mode 100644 index 0000000..a12c43b --- /dev/null +++ b/node/node_modules/node-static/examples/file-server.js @@ -0,0 +1,26 @@ +var sys = require('sys'); +var static = require('../lib/node-static'); + +// +// Create a node-static server to serve the current directory +// +var file = new(static.Server)('.', { cache: 7200, headers: {'X-Hello':'World!'} }); + +require('http').createServer(function (request, response) { + request.addListener('end', function () { + // + // Serve files! + // + file.serve(request, response, function (err, res) { + if (err) { // An error as occured + sys.error("> Error serving " + request.url + " - " + err.message); + response.writeHead(err.status, err.headers); + response.end(); + } else { // The file was served successfully + sys.puts("> " + request.url + " - " + res.message); + } + }); + }); +}).listen(8080); + +sys.puts("> node-static is listening on http://127.0.0.1:8080"); diff --git a/node/node_modules/node-static/lib/node-static.js b/node/node_modules/node-static/lib/node-static.js new file mode 100644 index 0000000..ef8ad5a --- /dev/null +++ b/node/node_modules/node-static/lib/node-static.js @@ -0,0 +1,252 @@ +var fs = require('fs'), + sys = require('sys'), + events = require('events'), + buffer = require('buffer'), + http = require('http'), + url = require('url'), + path = require('path'); + +this.version = [0, 5, 6]; + +var mime = require('./node-static/mime'); +var util = require('./node-static/util'); + +var serverInfo = 'node-static/' + this.version.join('.'); + +// In-memory file store +this.store = {}; +this.indexStore = {}; + +this.Server = function (root, options) { + if (root && (typeof(root) === 'object')) { options = root, root = null } + + this.root = path.normalize(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 (this.cache !== false) { + this.defaultHeaders['Cache-Control'] = 'max-age=' + this.cache; + } + this.defaultHeaders['Server'] = serverInfo; + + for (var k in this.defaultHeaders) { + this.options.headers[k] = this.options.headers[k] || + this.defaultHeaders[k]; + } +}; + +this.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) { + that.respond(null, 200, {}, [htmlIndex], stat, req, res, finish); + } else { + if (pathname in exports.store) { + streamFiles(exports.indexStore[pathname].files); + } 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); + exports.indexStore[pathname] = index; + streamFiles(index.files); + }); + } + } + }); + function streamFiles(files) { + util.mstat(pathname, files, function (e, stat) { + that.respond(pathname, 200, {}, files, stat, req, res, finish); + }); + } +}; +this.Server.prototype.serveFile = function (pathname, status, headers, req, res) { + var that = this; + var promise = new(events.EventEmitter); + + pathname = this.normalize(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; +}; +this.Server.prototype.finish = function (status, headers, req, res, promise, callback) { + var result = { + status: status, + headers: headers, + message: http.STATUS_CODES[status] + }; + + headers['Server'] = serverInfo; + + if (!status || status >= 400) { + if (callback) { + callback(result); + } else { + if (promise.listeners('error').length > 0) { + promise.emit('error', result); + } + 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); + } +}; + +this.Server.prototype.servePath = function (pathname, status, headers, req, res, finish) { + var that = this, + promise = new(events.EventEmitter); + + pathname = this.normalize(pathname); + + // Only allow GET and HEAD requests + if (req.method !== 'GET' && req.method !== 'HEAD') { + finish(405, { 'Allow': 'GET, HEAD' }); + return promise; + } + + // Make sure we're not trying to access a + // file outside of the root. + if (new(RegExp)('^' + that.root).test(pathname)) { + 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; +}; +this.Server.prototype.normalize = function (pathname) { + return path.normalize(path.join(this.root, pathname)); +}; +this.Server.prototype.serve = function (req, res, callback) { + var that = this, + promise = new(events.EventEmitter); + + var pathname = url.parse(req.url).pathname; + + var finish = function (status, headers) { + that.finish(status, headers, req, res, promise, callback); + }; + + 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 } +}; + +this.Server.prototype.respond = function (pathname, status, _headers, files, stat, req, res, finish) { + var mtime = Date.parse(stat.mtime), + key = pathname || files[0], + headers = {}; + + // Copy default headers + for (var k in this.options.headers) { headers[k] = this.options.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(); + + // Conditional GET + // If both the "If-Modified-Since" and "If-None-Match" headers + // match the conditions, send a 304 Not Modified. + if (req.headers['if-none-match'] === headers['Etag'] && + Date.parse(req.headers['if-modified-since']) >= mtime) { + finish(304, headers); + } else if (req.method === 'HEAD') { + finish(200, headers); + } else { + headers['Content-Length'] = stat.size; + headers['Content-Type'] = mime.contentTypes[path.extname(files[0]).slice(1)] || + 'application/octet-stream'; + + for (var k in headers) { _headers[k] = headers[k] } + + res.writeHead(status, _headers); + + // If the file was cached and it's not older + // than what's on disk, serve the cached version. + if (this.cache && (key in exports.store) && + exports.store[key].stat.mtime >= stat.mtime) { + res.end(exports.store[key].buffer); + finish(status, _headers); + } else { + this.stream(pathname, files, new(buffer.Buffer)(stat.size), res, function (e, buffer) { + if (e) { return finish(500, {}) } + exports.store[key] = { + stat: stat, + buffer: buffer, + timestamp: Date.now() + }; + finish(status, _headers); + }); + } + } +}; + +this.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); +}; diff --git a/node/node_modules/node-static/lib/node-static/mime.js b/node/node_modules/node-static/lib/node-static/mime.js new file mode 100644 index 0000000..cdd3355 --- /dev/null +++ b/node/node_modules/node-static/lib/node-static/mime.js @@ -0,0 +1,139 @@ +this.contentTypes = { + "aiff": "audio/x-aiff", + "arj": "application/x-arj-compressed", + "asf": "video/x-ms-asf", + "asx": "video/x-ms-asx", + "au": "audio/ulaw", + "avi": "video/x-msvideo", + "bcpio": "application/x-bcpio", + "ccad": "application/clariscad", + "cod": "application/vnd.rim.cod", + "com": "application/x-msdos-program", + "cpio": "application/x-cpio", + "cpt": "application/mac-compactpro", + "csh": "application/x-csh", + "css": "text/css", + "deb": "application/x-debian-package", + "dl": "video/dl", + "doc": "application/msword", + "drw": "application/drafting", + "dvi": "application/x-dvi", + "dwg": "application/acad", + "dxf": "application/dxf", + "dxr": "application/x-director", + "etx": "text/x-setext", + "ez": "application/andrew-inset", + "fli": "video/x-fli", + "flv": "video/x-flv", + "gif": "image/gif", + "gl": "video/gl", + "gtar": "application/x-gtar", + "gz": "application/x-gzip", + "hdf": "application/x-hdf", + "hqx": "application/mac-binhex40", + "html": "text/html", + "ice": "x-conference/x-cooltalk", + "ico": "image/x-icon", + "ief": "image/ief", + "igs": "model/iges", + "ips": "application/x-ipscript", + "ipx": "application/x-ipix", + "jad": "text/vnd.sun.j2me.app-descriptor", + "jar": "application/java-archive", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "js": "text/javascript", + "json": "application/json", + "latex": "application/x-latex", + "less": "text/css", + "lsp": "application/x-lisp", + "lzh": "application/octet-stream", + "m": "text/plain", + "m3u": "audio/x-mpegurl", + "man": "application/x-troff-man", + "manifest": "text/cache-manifest", + "me": "application/x-troff-me", + "midi": "audio/midi", + "mif": "application/x-mif", + "mime": "www/mime", + "movie": "video/x-sgi-movie", + "mp4": "video/mp4", + "mpg": "video/mpeg", + "mpga": "audio/mpeg", + "ms": "application/x-troff-ms", + "nc": "application/x-netcdf", + "oda": "application/oda", + "ogm": "application/ogg", + "pbm": "image/x-portable-bitmap", + "pdf": "application/pdf", + "pgm": "image/x-portable-graymap", + "pgn": "application/x-chess-pgn", + "pgp": "application/pgp", + "pm": "application/x-perl", + "png": "image/png", + "pnm": "image/x-portable-anymap", + "ppm": "image/x-portable-pixmap", + "ppz": "application/vnd.ms-powerpoint", + "pre": "application/x-freelance", + "prt": "application/pro_eng", + "ps": "application/postscript", + "qt": "video/quicktime", + "ra": "audio/x-realaudio", + "rar": "application/x-rar-compressed", + "ras": "image/x-cmu-raster", + "rgb": "image/x-rgb", + "rm": "audio/x-pn-realaudio", + "rpm": "audio/x-pn-realaudio-plugin", + "rtf": "text/rtf", + "rtx": "text/richtext", + "scm": "application/x-lotusscreencam", + "set": "application/set", + "sgml": "text/sgml", + "sh": "application/x-sh", + "shar": "application/x-shar", + "silo": "model/mesh", + "sit": "application/x-stuffit", + "skt": "application/x-koan", + "smil": "application/smil", + "snd": "audio/basic", + "sol": "application/solids", + "spl": "application/x-futuresplash", + "src": "application/x-wais-source", + "stl": "application/SLA", + "stp": "application/STEP", + "sv4cpio": "application/x-sv4cpio", + "sv4crc": "application/x-sv4crc", + "svg": "image/svg+xml", + "swf": "application/x-shockwave-flash", + "tar": "application/x-tar", + "tcl": "application/x-tcl", + "tex": "application/x-tex", + "texinfo": "application/x-texinfo", + "tgz": "application/x-tar-gz", + "tiff": "image/tiff", + "tr": "application/x-troff", + "tsi": "audio/TSP-audio", + "tsp": "application/dsptype", + "tsv": "text/tab-separated-values", + "txt": "text/plain", + "unv": "application/i-deas", + "ustar": "application/x-ustar", + "vcd": "application/x-cdlink", + "vda": "application/vda", + "vivo": "video/vnd.vivo", + "vrm": "x-world/x-vrml", + "wav": "audio/x-wav", + "wax": "audio/x-ms-wax", + "wma": "audio/x-ms-wma", + "wmv": "video/x-ms-wmv", + "wmx": "video/x-ms-wmx", + "wrl": "model/vrml", + "wvx": "video/x-ms-wvx", + "xbm": "image/x-xbitmap", + "xlw": "application/vnd.ms-excel", + "xml": "text/xml", + "xpm": "image/x-xpixmap", + "xwd": "image/x-xwindowdump", + "xyz": "chemical/x-pdb", + "zip": "application/zip" +}; diff --git a/node/node_modules/node-static/lib/node-static/util.js b/node/node_modules/node-static/lib/node-static/util.js new file mode 100644 index 0000000..8dd8e04 --- /dev/null +++ b/node/node_modules/node-static/lib/node-static/util.js @@ -0,0 +1,30 @@ +var fs = require('fs'), + path = require('path'); + +this.mstat = function (dir, files, callback) { + (function mstat(files, stats) { + var file = files.shift(); + + if (file) { + fs.stat(path.join(dir, file), function (e, stat) { + if (e) { + callback(e); + } else { + mstat(files, stats.concat([stat])); + } + }); + } else { + callback(null, { + size: stats.reduce(function (total, stat) { + return total + stat.size; + }, 0), + mtime: stats.reduce(function (latest, stat) { + return latest > stat.mtime ? latest : stat.mtime; + }, 0), + ino: stats.reduce(function (total, stat) { + return total + stat.ino; + }, 0) + }); + } + })(files.slice(0), []); +}; diff --git a/node/node_modules/node-static/package.json b/node/node_modules/node-static/package.json new file mode 100644 index 0000000..f79326a --- /dev/null +++ b/node/node_modules/node-static/package.json @@ -0,0 +1,15 @@ +{ + "name" : "node-static", + "description" : "simple, compliant file streaming module for node", + "url" : "http://github.com/cloudhead/node-static", + "keywords" : ["http", "static", "file", "server"], + "author" : "Alexis Sellier ", + "contributors" : [], + "licenses" : ["MIT"], + "dependencies" : [], + "lib" : "lib", + "main" : "./lib/node-static", + "version" : "0.5.6", + "directories" : { "test": "./test" }, + "engines" : { "node": ">= 0.4.1" } +} -- 2.25.1