Permalink
Browse files

add performance test with node-static

  • Loading branch information...
1 parent 5e16892 commit 2676e57132a9d30ca570f00155dfff3f7ea0e36f @tjanczuk tjanczuk committed Dec 23, 2011
View
18 test/performance/www/node-static/public/style.css
@@ -0,0 +1,18 @@
+body
+{
+ font-family: Arial;
+}
+th
+{
+ background: Green;
+ text-align: left;
+}
+td.price
+{
+ text-align: right;
+}
+td.summary
+{
+ font-weight: bold;
+ text-align: right;
+}
View
28 test/performance/www/node-static/server.js
@@ -0,0 +1,28 @@
+var http = require('http');
+var file = new(require('node-static').Server)(__dirname + '/public');
+
+var app = http.createServer(function (request, response) {
+ request.addListener('end', function() {
+ file.serve(request, response);
+ });
+});
+
+if (!process.env.mode) {
+ app.listen(process.env.PORT);
+}
+else {
+ var cluster = require('cluster');
+
+ if (cluster.isMaster) {
+ for (var i = 0; i < 4; i++) {
+ cluster.fork();
+ }
+
+ cluster.on('death', function(worker) {
+ cluster.log('worker ' + worker.pid + ' died');
+ });
+ }
+ else {
+ app.listen(31416);
+ }
+}
View
36 test/performance/www/node-static/wcat.ubr
@@ -0,0 +1,36 @@
+scenario
+{
+ name = "default";
+
+ warmup = 30;
+ duration = 30;
+ cooldown = 30;
+
+ default
+ {
+ setheader
+ {
+ name = "Connection";
+ value = "keep-alive";
+ }
+ setheader
+ {
+ name = "Host";
+ value = server();
+ }
+ port = 31416;
+ version = HTTP11;
+ statuscode = 200;
+ close = ka;
+ }
+
+ transaction
+ {
+ id = "order";
+ weight = 1000;
+ request
+ {
+ url = "/style.css";
+ }
+ }
+}
View
31 test/performance/www/node-static/web.config
@@ -0,0 +1,31 @@
+<configuration>
+ <appSettings>
+ <add key="NODE_PENDING_PIPE_INSTANCES" value="65535" />
+ </appSettings>
+ <system.webServer>
+ <handlers>
+ <add name="iisnode" path="server.js" verb="*" modules="iisnode" />
+ </handlers>
+ <iisnode
+ node_env="production"
+ loggingEnabled="false"
+ debuggingEnabled="false"
+ devErrorsEnabled="false"
+ nodeProcessCountPerApplication="4"
+ maxConcurrentRequestsPerProcess="999999999"
+ maxNamedPipeConnectionRetry="10"
+ />
+ <rewrite>
+ <rules>
+ <rule name="StaticContent" stopProcessing="true">
+ <match url="style.css" />
+ <action type="Rewrite" url="public/style.css" />
+ </rule>
+ <rule name="DynamicContent" stopProcessing="true">
+ <action type="Rewrite" url="server.js" />
+ </rule>
+ </rules>
+ </rewrite>
+ <urlCompression doStaticCompression="false" doDynamicCompression="false" />
+ </system.webServer>
+</configuration>
View
20 test/performance/www/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.
View
252 test/performance/www/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, 9];
+
+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.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 (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.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;
+};
+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.resolve(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 (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;
+};
+this.Server.prototype.resolve = function (pathname) {
+ return path.resolve(path.join(this.root, pathname));
+};
+this.Server.prototype.serve = function (req, res, callback) {
+ var that = this,
+ promise = new(events.EventEmitter);
+
+ var pathname = decodeURI(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 the "If-Modified-Since" or "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);
+};
View
139 test/performance/www/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"
+};
View
30 test/performance/www/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), []);
+};
View
15 test/performance/www/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 <self@cloudhead.net>",
+ "contributors" : [],
+ "licenses" : ["MIT"],
+ "dependencies" : [],
+ "lib" : "lib",
+ "main" : "./lib/node-static",
+ "version" : "0.5.9",
+ "directories" : { "test": "./test" },
+ "engines" : { "node": ">= 0.4.1" }
+}

0 comments on commit 2676e57

Please sign in to comment.