• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23
24// This test requires the program 'wrk'.
25const common = require('../common');
26
27const child_process = require('child_process');
28const result = child_process.spawnSync('wrk', ['-h']);
29if (result.error && result.error.code === 'ENOENT')
30  common.skip('test requires `wrk` to be installed first');
31
32const assert = require('assert');
33const http = require('http');
34const url = require('url');
35
36const body = 'hello world\n';
37const server = http.createServer((req, res) => {
38  res.writeHead(200, {
39    'Content-Length': body.length,
40    'Content-Type': 'text/plain',
41  });
42  res.write(body);
43  res.end();
44});
45
46let keepAliveReqSec = 0;
47let normalReqSec = 0;
48
49
50const runAb = (opts, callback) => {
51  const args = [
52    '-c', opts.concurrent || 50,
53    '-t', opts.threads || 2,
54    '-d', opts.duration || '5s',
55  ];
56
57  if (!opts.keepalive) {
58    args.push('-H');
59    args.push('Connection: close');
60  }
61
62  args.push(url.format({ hostname: '127.0.0.1',
63                         port: opts.port, protocol: 'http' }));
64
65  const child = child_process.spawn('wrk', args);
66  child.stderr.pipe(process.stderr);
67  child.stdout.setEncoding('utf8');
68
69  let stdout;
70
71  child.stdout.on('data', (data) => stdout += data);
72
73  child.on('close', (code, signal) => {
74    if (code) {
75      console.error(code, signal);
76      process.exit(code);
77      return;
78    }
79
80    let matches = /Requests\/sec:\s*(\d+)\./i.exec(stdout);
81    const reqSec = parseInt(matches[1]);
82
83    matches = /Keep-Alive requests:\s*(\d+)/i.exec(stdout);
84    let keepAliveRequests;
85    if (matches) {
86      keepAliveRequests = parseInt(matches[1]);
87    } else {
88      keepAliveRequests = 0;
89    }
90
91    callback(reqSec, keepAliveRequests);
92  });
93};
94
95server.listen(0, () => {
96  const port = server.address().port;
97  runAb({ keepalive: true, port: port }, (reqSec) => {
98    keepAliveReqSec = reqSec;
99
100    runAb({ keepalive: false, port: port }, (reqSec) => {
101      normalReqSec = reqSec;
102      server.close();
103    });
104  });
105});
106
107process.on('exit', () => {
108  assert.strictEqual(
109    normalReqSec > 50,
110    true,
111    `normalReqSec should be greater than 50, but got ${normalReqSec}`,
112  );
113  assert.strictEqual(
114    keepAliveReqSec > 50,
115    true,
116    `keepAliveReqSec should be greater than 50, but got ${keepAliveReqSec}`,
117  );
118  assert.strictEqual(
119    normalReqSec < keepAliveReqSec,
120    true,
121    'normalReqSec should be less than keepAliveReqSec, ' +
122    `but ${normalReqSec} is greater than ${keepAliveReqSec}`,
123  );
124});
125