Skip to content

Commit 7aedda9

Browse files
puzpuzpuzdanielleadams
authored andcommitted
benchmark: add simple https benchmark
Adds a simple benchmark for https server based on the http simple benchmark. Updates benchmarker integration for autocannon and wrk, so that they support https scheme. Also adds a new HTTPS section and improves HTTP/2 section in the benchmark guide. PR-URL: #36612 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 822ac48 commit 7aedda9

File tree

5 files changed

+132
-9
lines changed

5 files changed

+132
-9
lines changed

benchmark/_http-benchmarkers.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class AutocannonBenchmarker {
2929
for (const field in options.headers) {
3030
args.push('-H', `${field}=${options.headers[field]}`);
3131
}
32-
args.push(`http://127.0.0.1:${options.port}${options.path}`);
32+
const scheme = options.scheme || 'http';
33+
args.push(`${scheme}://127.0.0.1:${options.port}${options.path}`);
3334
const child = child_process.spawn(this.executable, args);
3435
return child;
3536
}
@@ -60,11 +61,12 @@ class WrkBenchmarker {
6061
const duration = typeof options.duration === 'number' ?
6162
Math.max(options.duration, 1) :
6263
options.duration;
64+
const scheme = options.scheme || 'http';
6365
const args = [
6466
'-d', duration,
6567
'-c', options.connections,
6668
'-t', Math.min(options.connections, require('os').cpus().length || 8),
67-
`http://127.0.0.1:${options.port}${options.path}`,
69+
`${scheme}://127.0.0.1:${options.port}${options.path}`,
6870
];
6971
for (const field in options.headers) {
7072
args.push('-H', `${field}: ${options.headers[field]}`);
@@ -90,8 +92,8 @@ class WrkBenchmarker {
9092
*/
9193
class TestDoubleBenchmarker {
9294
constructor(type) {
93-
// `type` is the type of benchmarker. Possible values are 'http' and
94-
// 'http2'.
95+
// `type` is the type of benchmarker. Possible values are 'http', 'https',
96+
// and 'http2'.
9597
this.name = `test-double-${type}`;
9698
this.executable = path.resolve(__dirname, '_test-double-benchmarker.js');
9799
this.present = fs.existsSync(this.executable);
@@ -101,8 +103,9 @@ class TestDoubleBenchmarker {
101103
create(options) {
102104
process.env.duration = process.env.duration || options.duration || 5;
103105

106+
const scheme = options.scheme || 'http';
104107
const env = {
105-
test_url: `http://127.0.0.1:${options.port}${options.path}`,
108+
test_url: `${scheme}://127.0.0.1:${options.port}${options.path}`,
106109
...process.env
107110
};
108111

@@ -179,6 +182,7 @@ const http_benchmarkers = [
179182
new WrkBenchmarker(),
180183
new AutocannonBenchmarker(),
181184
new TestDoubleBenchmarker('http'),
185+
new TestDoubleBenchmarker('https'),
182186
new TestDoubleBenchmarker('http2'),
183187
new H2LoadBenchmarker(),
184188
];

benchmark/_test-double-benchmarker.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
'use strict';
22

33
const myModule = process.argv[2];
4-
if (!['http', 'http2'].includes(myModule)) {
4+
if (!['http', 'https', 'http2'].includes(myModule)) {
55
throw new Error(`Invalid module for benchmark test double: ${myModule}`);
66
}
77

8+
let options;
9+
if (myModule === 'https') {
10+
options = { rejectUnauthorized: false };
11+
}
12+
813
const http = require(myModule);
914

1015
const duration = +process.env.duration;
@@ -33,8 +38,12 @@ function request(res, client) {
3338
}
3439

3540
function run() {
36-
if (http.get) { // HTTP
37-
http.get(url, request);
41+
if (http.get) { // HTTP or HTTPS
42+
if (options) {
43+
http.get(url, options, request);
44+
} else {
45+
http.get(url, request);
46+
}
3847
} else { // HTTP/2
3948
const client = http.connect(url);
4049
client.on('error', (e) => { throw e; });
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
'use strict';
2+
3+
const fixtures = require('../../test/common/fixtures');
4+
const https = require('https');
5+
6+
const options = {
7+
key: fixtures.readKey('rsa_private.pem'),
8+
cert: fixtures.readKey('rsa_cert.crt')
9+
};
10+
11+
const storedBytes = Object.create(null);
12+
const storedBuffer = Object.create(null);
13+
14+
module.exports = https.createServer(options, (req, res) => {
15+
// URL format: /<type>/<length>/<chunks>/chunkedEnc
16+
const params = req.url.split('/');
17+
const command = params[1];
18+
let body = '';
19+
const arg = params[2];
20+
const n_chunks = parseInt(params[3], 10);
21+
const chunkedEnc = params.length >= 5 && params[4] === '0' ? false : true;
22+
let status = 200;
23+
24+
let n, i;
25+
if (command === 'bytes') {
26+
n = ~~arg;
27+
if (n <= 0)
28+
throw new Error('bytes called with n <= 0');
29+
if (storedBytes[n] === undefined) {
30+
storedBytes[n] = 'C'.repeat(n);
31+
}
32+
body = storedBytes[n];
33+
} else if (command === 'buffer') {
34+
n = ~~arg;
35+
if (n <= 0)
36+
throw new Error('buffer called with n <= 0');
37+
if (storedBuffer[n] === undefined) {
38+
storedBuffer[n] = Buffer.allocUnsafe(n);
39+
for (i = 0; i < n; i++) {
40+
storedBuffer[n][i] = 'C'.charCodeAt(0);
41+
}
42+
}
43+
body = storedBuffer[n];
44+
} else {
45+
status = 404;
46+
body = 'not found\n';
47+
}
48+
49+
// example: https://localhost:port/bytes/512/4
50+
// sends a 512 byte body in 4 chunks of 128 bytes
51+
const len = body.length;
52+
if (chunkedEnc) {
53+
res.writeHead(status, {
54+
'Content-Type': 'text/plain',
55+
'Transfer-Encoding': 'chunked'
56+
});
57+
} else {
58+
res.writeHead(status, {
59+
'Content-Type': 'text/plain',
60+
'Content-Length': len.toString()
61+
});
62+
}
63+
// send body in chunks
64+
if (n_chunks > 1) {
65+
const step = Math.floor(len / n_chunks) || 1;
66+
for (i = 0, n = (n_chunks - 1); i < n; ++i)
67+
res.write(body.slice(i * step, i * step + step));
68+
res.end(body.slice((n_chunks - 1) * step));
69+
} else {
70+
res.end(body);
71+
}
72+
});

benchmark/https/simple.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
4+
const bench = common.createBenchmark(main, {
5+
type: ['bytes', 'buffer'],
6+
len: [4, 1024, 102400],
7+
chunks: [1, 4],
8+
c: [50, 500],
9+
chunkedEnc: [1, 0],
10+
benchmarker: ['test-double-https'],
11+
duration: 5
12+
});
13+
14+
function main({ type, len, chunks, c, chunkedEnc, duration }) {
15+
const server = require('../fixtures/simple-https-server.js')
16+
.listen(common.PORT)
17+
.on('listening', () => {
18+
const path = `/${type}/${len}/${chunks}/${chunkedEnc}`;
19+
20+
bench.http({
21+
path,
22+
connections: c,
23+
scheme: 'https',
24+
duration
25+
}, () => {
26+
server.close();
27+
});
28+
});
29+
}

doc/guides/writing-and-running-benchmarks.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
* [Prerequisites](#prerequisites)
66
* [HTTP Benchmark Requirements](#http-benchmark-requirements)
7+
* [HTTPS Benchmark Requirements](#https-benchmark-requirements)
8+
* [HTTP/2 Benchmark Requirements](#http2-benchmark-requirements)
79
* [Benchmark Analysis Requirements](#benchmark-analysis-requirements)
810
* [Running benchmarks](#running-benchmarks)
911
* [Running individual benchmarks](#running-individual-benchmarks)
@@ -43,13 +45,20 @@ benchmarker to be used should be specified by providing it as an argument:
4345

4446
`node benchmark/http/simple.js benchmarker=autocannon`
4547

48+
#### HTTPS Benchmark Requirements
49+
50+
To run the `https` benchmarks, one of `autocannon` or `wrk` benchmarkers must
51+
be used.
52+
53+
`node benchmark/https/simple.js benchmarker=autocannon`
54+
4655
#### HTTP/2 Benchmark Requirements
4756

4857
To run the `http2` benchmarks, the `h2load` benchmarker must be used. The
4958
`h2load` tool is a component of the `nghttp2` project and may be installed
5059
from [nghttp2.org][] or built from source.
5160

52-
`node benchmark/http2/simple.js benchmarker=autocannon`
61+
`node benchmark/http2/simple.js benchmarker=h2load`
5362

5463
### Benchmark Analysis Requirements
5564

0 commit comments

Comments
 (0)