diff --git a/benchmark/_http-benchmarkers.js b/benchmark/_http-benchmarkers.js index d0f192e75948b6..f25bcd319882f6 100644 --- a/benchmark/_http-benchmarkers.js +++ b/benchmark/_http-benchmarkers.js @@ -29,7 +29,8 @@ class AutocannonBenchmarker { for (const field in options.headers) { args.push('-H', `${field}=${options.headers[field]}`); } - args.push(`http://127.0.0.1:${options.port}${options.path}`); + const scheme = options.scheme || 'http'; + args.push(`${scheme}://127.0.0.1:${options.port}${options.path}`); const child = child_process.spawn(this.executable, args); return child; } @@ -60,11 +61,12 @@ class WrkBenchmarker { const duration = typeof options.duration === 'number' ? Math.max(options.duration, 1) : options.duration; + const scheme = options.scheme || 'http'; const args = [ '-d', duration, '-c', options.connections, '-t', Math.min(options.connections, require('os').cpus().length || 8), - `http://127.0.0.1:${options.port}${options.path}`, + `${scheme}://127.0.0.1:${options.port}${options.path}`, ]; for (const field in options.headers) { args.push('-H', `${field}: ${options.headers[field]}`); @@ -90,8 +92,8 @@ class WrkBenchmarker { */ class TestDoubleBenchmarker { constructor(type) { - // `type` is the type of benchmarker. Possible values are 'http' and - // 'http2'. + // `type` is the type of benchmarker. Possible values are 'http', 'https', + // and 'http2'. this.name = `test-double-${type}`; this.executable = path.resolve(__dirname, '_test-double-benchmarker.js'); this.present = fs.existsSync(this.executable); @@ -101,8 +103,9 @@ class TestDoubleBenchmarker { create(options) { process.env.duration = process.env.duration || options.duration || 5; + const scheme = options.scheme || 'http'; const env = { - test_url: `http://127.0.0.1:${options.port}${options.path}`, + test_url: `${scheme}://127.0.0.1:${options.port}${options.path}`, ...process.env }; @@ -179,6 +182,7 @@ const http_benchmarkers = [ new WrkBenchmarker(), new AutocannonBenchmarker(), new TestDoubleBenchmarker('http'), + new TestDoubleBenchmarker('https'), new TestDoubleBenchmarker('http2'), new H2LoadBenchmarker(), ]; diff --git a/benchmark/_test-double-benchmarker.js b/benchmark/_test-double-benchmarker.js index 60264dfd46a606..89843d4616cc50 100644 --- a/benchmark/_test-double-benchmarker.js +++ b/benchmark/_test-double-benchmarker.js @@ -1,10 +1,15 @@ 'use strict'; const myModule = process.argv[2]; -if (!['http', 'http2'].includes(myModule)) { +if (!['http', 'https', 'http2'].includes(myModule)) { throw new Error(`Invalid module for benchmark test double: ${myModule}`); } +let options; +if (myModule === 'https') { + options = { rejectUnauthorized: false }; +} + const http = require(myModule); const duration = +process.env.duration; @@ -33,8 +38,12 @@ function request(res, client) { } function run() { - if (http.get) { // HTTP - http.get(url, request); + if (http.get) { // HTTP or HTTPS + if (options) { + http.get(url, options, request); + } else { + http.get(url, request); + } } else { // HTTP/2 const client = http.connect(url); client.on('error', (e) => { throw e; }); diff --git a/benchmark/fixtures/simple-https-server.js b/benchmark/fixtures/simple-https-server.js new file mode 100644 index 00000000000000..d3b07a110113d7 --- /dev/null +++ b/benchmark/fixtures/simple-https-server.js @@ -0,0 +1,72 @@ +'use strict'; + +const fixtures = require('../../test/common/fixtures'); +const https = require('https'); + +const options = { + key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt') +}; + +const storedBytes = Object.create(null); +const storedBuffer = Object.create(null); + +module.exports = https.createServer(options, (req, res) => { + // URL format: ////chunkedEnc + const params = req.url.split('/'); + const command = params[1]; + let body = ''; + const arg = params[2]; + const n_chunks = parseInt(params[3], 10); + const chunkedEnc = params.length >= 5 && params[4] === '0' ? false : true; + let status = 200; + + let n, i; + if (command === 'bytes') { + n = ~~arg; + if (n <= 0) + throw new Error('bytes called with n <= 0'); + if (storedBytes[n] === undefined) { + storedBytes[n] = 'C'.repeat(n); + } + body = storedBytes[n]; + } else if (command === 'buffer') { + n = ~~arg; + if (n <= 0) + throw new Error('buffer called with n <= 0'); + if (storedBuffer[n] === undefined) { + storedBuffer[n] = Buffer.allocUnsafe(n); + for (i = 0; i < n; i++) { + storedBuffer[n][i] = 'C'.charCodeAt(0); + } + } + body = storedBuffer[n]; + } else { + status = 404; + body = 'not found\n'; + } + + // example: https://localhost:port/bytes/512/4 + // sends a 512 byte body in 4 chunks of 128 bytes + const len = body.length; + if (chunkedEnc) { + res.writeHead(status, { + 'Content-Type': 'text/plain', + 'Transfer-Encoding': 'chunked' + }); + } else { + res.writeHead(status, { + 'Content-Type': 'text/plain', + 'Content-Length': len.toString() + }); + } + // send body in chunks + if (n_chunks > 1) { + const step = Math.floor(len / n_chunks) || 1; + for (i = 0, n = (n_chunks - 1); i < n; ++i) + res.write(body.slice(i * step, i * step + step)); + res.end(body.slice((n_chunks - 1) * step)); + } else { + res.end(body); + } +}); diff --git a/benchmark/https/simple.js b/benchmark/https/simple.js new file mode 100644 index 00000000000000..243546c346f484 --- /dev/null +++ b/benchmark/https/simple.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + type: ['bytes', 'buffer'], + len: [4, 1024, 102400], + chunks: [1, 4], + c: [50, 500], + chunkedEnc: [1, 0], + benchmarker: ['test-double-https'], + duration: 5 +}); + +function main({ type, len, chunks, c, chunkedEnc, duration }) { + const server = require('../fixtures/simple-https-server.js') + .listen(common.PORT) + .on('listening', () => { + const path = `/${type}/${len}/${chunks}/${chunkedEnc}`; + + bench.http({ + path, + connections: c, + scheme: 'https', + duration + }, () => { + server.close(); + }); + }); +} diff --git a/doc/guides/writing-and-running-benchmarks.md b/doc/guides/writing-and-running-benchmarks.md index b6b22d75c17e2c..ef93714899e534 100644 --- a/doc/guides/writing-and-running-benchmarks.md +++ b/doc/guides/writing-and-running-benchmarks.md @@ -4,6 +4,8 @@ * [Prerequisites](#prerequisites) * [HTTP Benchmark Requirements](#http-benchmark-requirements) + * [HTTPS Benchmark Requirements](#https-benchmark-requirements) + * [HTTP/2 Benchmark Requirements](#http2-benchmark-requirements) * [Benchmark Analysis Requirements](#benchmark-analysis-requirements) * [Running benchmarks](#running-benchmarks) * [Running individual benchmarks](#running-individual-benchmarks) @@ -43,13 +45,20 @@ benchmarker to be used should be specified by providing it as an argument: `node benchmark/http/simple.js benchmarker=autocannon` +#### HTTPS Benchmark Requirements + +To run the `https` benchmarks, one of `autocannon` or `wrk` benchmarkers must +be used. + +`node benchmark/https/simple.js benchmarker=autocannon` + #### HTTP/2 Benchmark Requirements To run the `http2` benchmarks, the `h2load` benchmarker must be used. The `h2load` tool is a component of the `nghttp2` project and may be installed from [nghttp2.org][] or built from source. -`node benchmark/http2/simple.js benchmarker=autocannon` +`node benchmark/http2/simple.js benchmarker=h2load` ### Benchmark Analysis Requirements