Permalink
Browse files

Initial commit

  • Loading branch information...
cloudhead
cloudhead committed Jul 27, 2010
0 parents commit 98965bb0baf22a442330078873b6dce17cbdf19c
Showing with 291 additions and 0 deletions.
  1. +20 −0 LICENSE
  2. +22 −0 examples/file-server.js
  3. +99 −0 lib/node-static.js
  4. +135 −0 lib/node-static/mime.js
  5. +15 −0 package.json
20 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.
@@ -0,0 +1,22 @@
+var sys = require('sys');
+var static = require('../lib/node-static');
+
+//
+// Create a node-static server instance to serve the './public' folder
+//
+var file = new(static.Server)();
+
+require('http').createServer(function (request, response) {
+ request.addListener('end', function () {
+ //
+ // Serve files!
+ //
+ file.serve(request, response, function (status, headers) {
+ sys.error("> Error serving " + request.url + " - " + status);
+ response.writeHead(status, headers);
+ response.end();
+ });
+ });
+}).listen(8080);
+
+sys.puts("> node-static is listening on http://127.0.0.1:8080");
@@ -0,0 +1,99 @@
+var fs = require('fs'),
+ url = require('url'),
+ path = require('path');
+
+var mime = require('./node-static/mime');
+
+this.Server = function (root, options) {
+ this.root = path.normalize(root || '.');
+ this.options = options || {};
+ this.logger = this.options.logger || function () {};
+};
+
+this.Server.prototype.serve = function (req, res, errback) {
+ var that = this;
+
+ process.nextTick(function () {
+ var file = url.parse(req.url).pathname;
+
+ // Only allow GET and HEAD requests
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
+ error(405, { 'Allow': 'GET, HEAD' });
+ }
+
+ // If we're trying to load a directory, look for
+ // an index.html inside of it.
+ if (/\/$/.test(file)) { file += 'index.html' }
+
+ file = path.normalize(path.join(that.root, file));
+
+ // Make sure we're not trying to access a
+ // file outside of the root.
+ if (new(RegExp)('^' + that.root).test(file)) {
+ fs.stat(file, function (e, stat) {
+ if (e || !stat.isFile()) {
+ error(404, {});
+ } else {
+ that.stream(file, stat, req, res);
+ }
+ });
+ } else {
+ // Forbidden
+ error(403, {});
+ }
+ });
+
+ function error(status, headers) {
+ if (typeof(errback) === 'function') {
+ errback(status, headers);
+ } else {
+ res.writeHead(status, headers);
+ res.end();
+ }
+ }
+};
+
+this.Server.prototype.stream = function (file, stat, req, res) {
+ var mtime = Date.parse(stat.mtime);
+ var headers = {
+ 'Etag': JSON.stringify([stat.ino, stat.size, mtime].join('-')),
+ 'Date': new(Date)().toUTCString(),
+ 'Server': 'node-static/' + exports.version.join('.'),
+ 'cache-control': 'max-age=' + this.options.cache || 3600,
+ '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) {
+ res.writeHead(304, headers);
+ res.end();
+ } else if (req.method === 'HEAD') {
+ res.writeHead(200, headers);
+ res.end();
+ } else {
+ headers['Content-Length'] = stat.size;
+ headers['Content-Type'] = mime.contentTypes[path.extname(file).slice(1)] ||
+ 'application/octet-stream';
+
+ res.writeHead(200, headers);
+
+ // Stream the file to the client
+ fs.createReadStream(file, {
+ flags: 'r',
+ encoding: 'binary',
+ mode: 0666,
+ bufferSize: 4096
+ }).addListener('data', function (chunk) {
+ res.write(chunk, 'binary');
+ }).addListener('close', function () {
+ res.end();
+ }).addListener('error', function (err) {
+ sys.error(err);
+ });
+ }
+};
+
+this.version = [0, 1, 0];
@@ -0,0 +1,135 @@
+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",
+ "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",
+ "lsp": "application/x-lisp",
+ "lzh": "application/octet-stream",
+ "m": "text/plain",
+ "m3u": "audio/x-mpegurl",
+ "man": "application/x-troff-man",
+ "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",
+ "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"
+};
@@ -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 <self@cloudhead.net>",
+ "contributors" : [],
+ "licenses" : ["MIT"],
+ "dependencies" : [],
+ "lib" : "lib",
+ "main" : "./lib/node-static",
+ "version" : "0.1.0",
+ "directories" : { "lib": "./lib/node-static", "test": "./test" },
+ "engines" : { "node": "> 0.1.98" }
+}

0 comments on commit 98965bb

Please sign in to comment.