gohttp
Version:
http & https client for HTTP/1.1 and HTTP/2
131 lines (115 loc) • 4.81 kB
JavaScript
;
const cluster = require('node:cluster');
const os = require('node:os');
const { URL } = require('node:url');
const { performance } = require('node:perf_hooks');
const { http2Connect, GoHttp} = require('../index.js');
const args = process.argv.slice(2);
const config = {
url: '', method: 'GET', type: 'h1', data: null, file: null, uploadName: 'file',
concurrency: 1, total: 100, processes: os.cpus().length, headers: {}, verifyCert: true
};
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '-u': config.url = args[++i]; break;
case '-t': config.type = args[++i]; break;
case '-m': config.method = args[++i].toUpperCase(); break;
case '-d': config.data = args[++i]; break;
case '-f': config.file = args[++i]; break;
case '-up': config.uploadName = args[++i]; break;
case '-i': config.verifyCert = false; break;
case '-c': config.concurrency = parseInt(args[++i]); break;
case '-n': config.total = parseInt(args[++i]); break;
case '-p': config.processes = parseInt(args[++i]); break;
case '-H': const p = args[++i].split(':'); if(p.length>=2) config.headers[p.shift().trim()]=p.join(':').trim(); break;
}
}
if (!config.url && cluster.isMaster) { console.log('Usage: httpbench -u <url> [-c 100 -n 10000 -p 8 -t h2]'); process.exit(1); }
if (config.file && config.method === 'GET') config.method = 'POST';
if (config.data) { try { config.data=JSON.parse(config.data); if(!config.headers['content-type']) config.headers['content-type']='application/json'; }catch(e){} }
let h1cli = new GoHttp({
verifyCert: config.verifyCert
})
// --- Worker ---
if (cluster.isWorker) {
process.on('message', async (msg) => {
if (msg.cmd !== 'start') return;
const isH2 = config.type === 'h2';
const client = isH2 ? http2Connect(config.url, {
verifyCert: config.verifyCert,
rejectUnauthorized: config.verifyCert})
: h1cli;
let ok = 0, fail = 0, processed = 0, remaining = msg.total;
const workerLoop = async () => {
while (true) {
if (remaining <= 0) break;
remaining--;
try {
let r;
const opts = { method: config.method, headers: { ...config.headers } };
if (config.file) {
if (!isH2) {
r = await client.up(config.url, {
...opts, file: config.file, name: config.uploadName
})
} {
r = await client.up({
...opts,
file: config.file,
name: config.uploadName
});
}
} else {
if (config.data) opts.body = config.data;
if (isH2) { const u = new URL(config.url); opts.path = u.pathname+u.search; r=await client.request(opts); }
else r = await client.request(config.url, opts);
}
if (r.ok) ok++; else fail++;
} catch (e) { fail++; }
processed++;
if (processed % 100 === 0) { process.send({ t: 'p', c: 100 }); processed = 0; }
}
};
const ths = [];
for(let i=0; i<config.concurrency; i++) ths.push(workerLoop());
await Promise.all(ths);
process.send({ t: 'p', c: processed });
process.send({ t: 'd', ok, fail });
if (isH2 && client) client.close();
process.exit(0);
});
}
// --- Master ---
if (cluster.isMaster) {
console.log(`\x1b[33m[httpbench] Launching ${config.processes} processes...\x1b[0m`);
console.log(`Target: ${config.url} (${config.type})`);
console.log(`Load: ${config.concurrency} conc x ${config.processes} cores = ${config.concurrency*config.processes} concurrent`);
console.log(`Total: ${config.total} requests`);
const t0 = performance.now();
let done = 0, okTotal = 0, failTotal = 0, active = config.processes;
const base = Math.floor(config.total / config.processes);
let rem = config.total % config.processes;
for (let i = 0; i < config.processes; i++) {
const w = cluster.fork();
w.send({ cmd: 'start', total: base + (rem-- > 0 ? 1 : 0) });
w.on('message', m => {
if (m.t === 'p') {
done += m.c;
if (done % Math.ceil(config.total/20) === 0) process.stdout.write('.');
} else if (m.t === 'd') {
okTotal += m.ok; failTotal += m.fail;
if (--active === 0) report();
}
});
}
function report() {
const s = (performance.now() - t0) / 1000;
console.log('\n\n\x1b[32m=== Benchmark Result ===\x1b[0m');
console.log(`Time: ${s.toFixed(3)} s`);
console.log(`QPS: \x1b[36m${(done/s).toFixed(2)}\x1b[0m`);
console.log(`Success: ${okTotal}`);
console.log(`Fail: ${failTotal}`);
process.exit(0);
}
}