var gauss = require('gauss'); var url_parse = require('url').parse; var iniparser = require('iniparser'); var extend = require('xtend'); var http = require('http'); var fs = require('fs'); var async = require('async'); var argv = require('optimist') .usage('Usage: $0 [config] [config]...') .options('c', { alias: 'concurrency', default: 1, describe: 'Number of Concurrent Clients to Use' }) .options('r', { alias: 'requests', describe: 'Total Number Of Requests To Make' }) .options('k', { alias: 'keepalive', describe: 'Whether Or Not To Enable Keep Alive Support', boolean: true, default: false, }) .options('l', { alias: 'log', describe: 'Log File To Write All Request Data To', string: true, }) .options('s', { alias: 'stats', describe: 'Whether or Not Print Result Stats', boolean: true, default: true }) .options('q', { alias: 'quiet', default: false, describe: 'Whether Or Not To Silence stdout', boolean: true, }) .options('h', { alias: 'help', describe: 'Show This Help Text', boolean: true }) .demand(1) .argv; if( argv.help ){ require('optimist').showHelp(); process.exit(); } if( argv.quiet ){ console.log = function(msg){}; } if( argv.log ){ argv.log = fs.openSync(argv.log, 'w'); } var config = { random: false, urls: {}, }; var defaults = { num: 1, port: 80, }; argv._.forEach(function(file){ try{ ini = iniparser.parseSync(file); for( var key in ini ){ if( typeof(ini[key]) === 'object'){ var url = key; if(url.substring(0, 4) !== 'http'){ url = 'http://' + url; } parts = url_parse(url, true, true); ini[key] = extend({}, defaults, ini[key], parts); } else{ config[key] = ini[key]; delete ini[key]; } } config.urls = extend(config.urls, ini); }catch(e){ console.error('Could Not Process ' + file + ' ini File'); console.error(e.stack); process.exit(1); } }); var urls = []; for( var key in config.urls ){ var url = config.urls[key]; for( var i = 0; i < url.num; ++i ){ urls.push(url); } } if( typeof(config.random) !== 'boolean' ){ config.random = config.random.toLowerCase() === 'true'; } if( config.random ){ console.log('Randomizing Url List'); var i = urls.length; if ( i == 0 ) return false; while ( --i ) { var j = Math.floor( Math.random() * ( i + 1 ) ); var tempi = urls[i]; var tempj = urls[j]; urls[i] = tempj; urls[j] = tempi; } } if( !argv.requests ){ argv.requests = urls.length; } var results = []; var errors = []; var save_result = function(result){ results.push(result); if( argv.log ){ var tmp = []; for( var i in result ){ var val = parseInt(result[i]); if( isNaN(val) ){ tmp.push('"' + result[i] + '"'); } else{ tmp.push(val); } } fs.writeSync(argv.log, tmp.join() + '\r\n'); } } var run = function(options, done){ var start = new Date().getTime(); req = http.request(options, function(res){ var stop = new Date().getTime(); save_result({ ms: (stop - start), status: res.statusCode, url: options.href }); done(); }); req.on('error', function(err){ save_error({ error: err, url: options.href }); done(); }); if( options.data ){ req.write(options.data); } req.setSocketKeepAlive(argv.keepalive); req.end(); }; console.log('Starting Up ' + argv.concurrency + ' Clients'); var queue = async.queue(run, argv.concurrency); var percentiles = [.7, .8, .85, .9, .94, .96, .99]; queue.drain = function(){ console.log('Finished'); if( argv.stats ){ console.log('Processing Results'); var tmp = results; results = {}; tmp.forEach( function(result){ var key = result.url; delete result.url; if( results[key] == undefined ){ results[key] = [] } results[key].push(result); }); for( var url in results ){ console.log('Results For: ' + url); console.log('\tTotal Requests: ' + results[url].length); var ms_data = results[url].map( function(a){ return a.ms; } ); ms_data = ms_data.sort(); var ms_set = new gauss.Vector(ms_data); console.log('\tMean Time (ms): ' + ms_set.mean()); console.log('\tMin Time (ms): ' + ms_set.min()); console.log('\tMax Time (ms): ' + ms_set.max()); console.log('\tTotal Time (ms): ' + ms_set.sum()); percentiles.forEach( function(percent){ console.log('\t' + (percent*100) + 'th Percentile: ' + ms_set.percentile(percent)); }); } } if( argv.log ){ fs.closeSync(argv.log); } process.exit(0); }; console.log('Queuing Up ' + argv.requests + ' Requests'); var offset = 0; for( var i = 0; i < argv.requests; ++i ){ if( offset >= urls.length ){ offset = 0; } queue.push(urls[offset]); offset += 1; }