зеркало из https://github.com/Azure/iisnode.git
add performance test with node-static
This commit is contained in:
Родитель
5e16892559
Коммит
2676e57132
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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.
|
252
test/performance/www/node_modules/node-static/lib/node-static.js
сгенерированный
поставляемый
Normal file
252
test/performance/www/node_modules/node-static/lib/node-static.js
сгенерированный
поставляемый
Normal file
|
@ -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);
|
||||
};
|
139
test/performance/www/node_modules/node-static/lib/node-static/mime.js
сгенерированный
поставляемый
Normal file
139
test/performance/www/node_modules/node-static/lib/node-static/mime.js
сгенерированный
поставляемый
Normal file
|
@ -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"
|
||||
};
|
30
test/performance/www/node_modules/node-static/lib/node-static/util.js
сгенерированный
поставляемый
Normal file
30
test/performance/www/node_modules/node-static/lib/node-static/util.js
сгенерированный
поставляемый
Normal file
|
@ -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), []);
|
||||
};
|
15
test/performance/www/node_modules/node-static/package.json
сгенерированный
поставляемый
Normal file
15
test/performance/www/node_modules/node-static/package.json
сгенерированный
поставляемый
Normal file
|
@ -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" }
|
||||
}
|
Загрузка…
Ссылка в новой задаче