Skip to content

Commit 2854067

Browse files
committed
Merge branch 'master' of github.com:mcollina/autocannon
2 parents 87ac52e + 3167fab commit 2854067

14 files changed

+291
-32
lines changed

.github/workflows/nodejs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
fail-fast: false
1717
matrix:
1818
os: [ubuntu-latest, windows-latest, macos-latest]
19-
node-version: [14, 16, 18, 20]
19+
node-version: [20, 22]
2020

2121
steps:
2222
- uses: actions/checkout@v3

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Available options:
6262
Start the command listed after -- on the command line. When it starts listening on a port,
6363
start sending requests to that port. A URL is still required to send requests to
6464
the correct path. The hostname can be omitted, `localhost` will be used by default.
65+
If the command after -- is `node <script>`, this flag is optional and assumed to be `true`.
6566
-m/--method METHOD
6667
The HTTP method to use. default: 'GET'.
6768
-t/--timeout NUM
@@ -113,7 +114,7 @@ Available options:
113114
-l/--latency
114115
Print all the latency data. default: false.
115116
-I/--idReplacement
116-
Enable replacement of [<id>] with a randomly generated ID within the request body. default: false.
117+
Enable replacement of `[<id>]` with a randomly generated ID within the request body. e.g. `/items/[<id>]`. default: false.
117118
-j/--json
118119
Print the output as newline delimited JSON. This will cause the progress bar and results not to be rendered. default: false.
119120
-f/--forever

autocannon.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const URL = require('url').URL
1111
const spawn = require('child_process').spawn
1212
const managePath = require('manage-path')
1313
const hasAsyncHooks = require('has-async-hooks')
14-
const subarg = require('subarg')
14+
const subarg = require('@minimistjs/subarg')
1515
const printResult = require('./lib/printResult')
1616
const initJob = require('./lib/init')
1717
const track = require('./lib/progressTracker')
@@ -115,6 +115,11 @@ function parseArguments (argvs) {
115115

116116
argv.url = argv._.length > 1 ? argv._ : argv._[0]
117117

118+
// Assume onPort if `-- node` is provided
119+
if (argv['--'][0] === 'node') {
120+
argv.onPort = true
121+
}
122+
118123
if (argv.onPort) {
119124
argv.spawn = argv['--']
120125
}

lib/printResult.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,27 @@ const defaults = {
1515
verbose: true
1616
}
1717

18+
class TableWithoutColor extends Table {
19+
constructor (opts = {}) {
20+
super({ ...opts, style: { head: [], border: [] } })
21+
}
22+
}
23+
1824
const printResult = (result, opts) => {
1925
opts = Object.assign({}, defaults, opts)
2026
let strResult = ''
2127

2228
if (opts.verbose) {
2329
const chalk = new Chalk.Instance(testColorSupport({ stream: opts.outputStream, alwaysReturn: true }))
30+
const ColorSafeTable = chalk.level === 0 ? TableWithoutColor : Table
2431

25-
const shortLatency = new Table({
32+
const shortLatency = new ColorSafeTable({
2633
head: asColor(chalk.cyan, ['Stat', '2.5%', '50%', '97.5%', '99%', 'Avg', 'Stdev', 'Max'])
2734
})
2835
shortLatency.push(asLowRow(chalk.bold('Latency'), asMs(result.latency)))
2936
logToLocalStr('\n' + shortLatency.toString())
3037

31-
const requests = new Table({
38+
const requests = new ColorSafeTable({
3239
head: asColor(chalk.cyan, ['Stat', '1%', '2.5%', '50%', '97.5%', 'Avg', 'Stdev', 'Min'])
3340
})
3441

@@ -37,7 +44,7 @@ const printResult = (result, opts) => {
3744
logToLocalStr(requests.toString())
3845

3946
if (opts.renderStatusCodes === true) {
40-
const statusCodeStats = new Table({
47+
const statusCodeStats = new ColorSafeTable({
4148
head: asColor(chalk.cyan, ['Code', 'Count'])
4249
})
4350
Object.keys(result.statusCodeStats).forEach(statusCode => {
@@ -57,7 +64,7 @@ const printResult = (result, opts) => {
5764
logToLocalStr('')
5865

5966
if (opts.renderLatencyTable) {
60-
const latencies = new Table({
67+
const latencies = new ColorSafeTable({
6168
head: asColor(chalk.cyan, ['Percentile', 'Latency (ms)'])
6269
})
6370
percentiles.map((perc) => {
@@ -85,7 +92,7 @@ const printResult = (result, opts) => {
8592
logToLocalStr(`${format(result.mismatches)} requests with mismatched body`)
8693
}
8794
if (result.resets) {
88-
logToLocalStr(`request pipeline was resetted ${format(result.resets)} ${result.resets === 1 ? 'time' : 'times'}`)
95+
logToLocalStr(`request pipeline was reset ${format(result.resets)} ${result.resets === 1 ? 'time' : 'times'}`)
8996
}
9097

9198
function logToLocalStr (msg) {

lib/requestIterator.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const hyperid = require('hyperid')(true)
3+
const hyperid = require('hyperid')
44
const inherits = require('util').inherits
55
const requestBuilder = require('./httpRequestBuilder')
66
const clone = require('lodash.clonedeep')
@@ -26,6 +26,7 @@ function RequestIterator (opts) {
2626
return new RequestIterator(opts)
2727
}
2828

29+
this.hyperid = hyperid({ urlSafe: true })
2930
this.resetted = false
3031
this.headers = {}
3132
this.initialContext = opts.initialContext || {}
@@ -107,8 +108,12 @@ RequestIterator.prototype.rebuildRequest = function () {
107108
this.currentRequest.headers = this.currentRequest.headers || this.headers
108109
data = this.requestBuilder(this.currentRequest, this.context)
109110
if (data) {
111+
const hyperid = this.hyperid()
110112
this.currentRequest.requestBuffer = this.reqDefaults.idReplacement
111-
? Buffer.from(data.toString().replace(/\[<id>\]/g, hyperid()))
113+
? Buffer.from(data.toString()
114+
.replace(/\[<id>\]/g, hyperid)
115+
// in the first line only (the url), replace encoded id placeholders
116+
.replace(/^.+/, m => m.replace(/\[%3Cid%3E]/g, hyperid)))
112117
: data
113118
} else if (this.currentRequestIndex === 0) {
114119
// when first request fails to build, we can not reset pipeline, or it'll never end

lib/validate.js

+4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ module.exports = function validateOpts (opts, cbPassedIn) {
6666
// fill in defaults after
6767
opts = defaultOpts(opts)
6868

69+
if (opts.json === true) {
70+
opts.renderProgressBar = opts.renderResultsTable = opts.renderLatencyTable = false
71+
}
72+
6973
if (opts.requests) {
7074
if (opts.requests.some(r => !isValidFn(r.setupRequest))) {
7175
return new Error('Invalid option setupRequest, please provide a function (or file path when in workers mode)')

package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,27 @@
3030
"contributors": [
3131
"Glen Keane <[email protected]>",
3232
"Donald Robertson <[email protected]",
33-
"Salman Mitha <[email protected]"
33+
"Salman Mitha <[email protected]>"
3434
],
3535
"license": "MIT",
3636
"bugs": {
3737
"url": "https://github.com/mcollina/autocannon/issues"
3838
},
3939
"homepage": "https://github.com/mcollina/autocannon#readme",
4040
"devDependencies": {
41+
"ansi-regex": "^5.0.1",
4142
"bl": "^6.0.0",
4243
"busboy": "^0.3.1",
4344
"pre-commit": "^1.1.2",
4445
"proxyquire": "^2.1.3",
4546
"sinon": "^15.0.0",
4647
"split2": "^4.0.0",
4748
"standard": "^17.0.0",
48-
"tap": "^16.0.0"
49+
"tap": "^16.0.0",
50+
"why-is-node-running": "^2.3.0"
4951
},
5052
"dependencies": {
53+
"@minimistjs/subarg": "^1.0.0",
5154
"chalk": "^4.1.0",
5255
"char-spinner": "^1.0.1",
5356
"cli-table3": "^0.6.0",
@@ -69,7 +72,6 @@
6972
"reinterval": "^1.1.0",
7073
"retimer": "^3.0.0",
7174
"semver": "^7.3.2",
72-
"subarg": "^1.0.0",
7375
"timestring": "^6.0.0"
7476
}
7577
}

test/cli.test.js

+24-8
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ test('should run benchmark against server', (t) => {
4747
})
4848

4949
t.teardown(() => {
50-
child.kill()
50+
try {
51+
child.kill()
52+
} catch {}
5153
})
5254

5355
child
@@ -102,7 +104,9 @@ test('should parse HAR file and run requests', (t) => {
102104
})
103105

104106
t.teardown(() => {
105-
child.kill()
107+
try {
108+
child.kill()
109+
} catch {}
106110
})
107111

108112
child
@@ -126,7 +130,9 @@ test('should throw on unknown HAR file', (t) => {
126130
})
127131

128132
t.teardown(() => {
129-
child.kill()
133+
try {
134+
child.kill()
135+
} catch {}
130136
})
131137

132138
const lines = []
@@ -155,7 +161,9 @@ test('should throw on invalid HAR file', (t) => {
155161
})
156162

157163
t.teardown(() => {
158-
child.kill()
164+
try {
165+
child.kill()
166+
} catch {}
159167
})
160168

161169
const lines = []
@@ -187,7 +195,9 @@ test('should write warning about unused HAR requests', (t) => {
187195
})
188196

189197
t.teardown(() => {
190-
child.kill()
198+
try {
199+
child.kill()
200+
} catch {}
191201
})
192202

193203
const lines = []
@@ -241,7 +251,9 @@ test('run with workers', { skip: !hasWorkerSupport }, (t) => {
241251
})
242252

243253
t.teardown(() => {
244-
child.kill()
254+
try {
255+
child.kill()
256+
} catch {}
245257
})
246258

247259
child
@@ -278,7 +290,9 @@ test('should run handle PUT bodies', (t) => {
278290
})
279291

280292
t.teardown(() => {
281-
child.kill()
293+
try {
294+
child.kill()
295+
} catch {}
282296
})
283297

284298
const outputLines = []
@@ -323,7 +337,9 @@ test('should run handle PUT bodies', (t) => {
323337
})
324338

325339
t.teardown(() => {
326-
child.kill()
340+
try {
341+
child.kill()
342+
} catch {}
327343
})
328344

329345
const outputLines = []

test/envPort.test.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
'use strict'
22

3+
const why = require('why-is-node-running')
34
const t = require('tap')
45
const split = require('split2')
56
const path = require('path')
67
const childProcess = require('child_process')
78
const helper = require('./helper')
89

10+
setInterval(function () {
11+
console.log(why())
12+
}, 30000).unref()
13+
914
const lines = [
1015
/Running 1s test @ .*$/,
1116
/10 connections.*$/,
@@ -46,7 +51,9 @@ const child = childProcess.spawn(process.execPath, [path.join(__dirname, '..'),
4651
})
4752

4853
t.teardown(() => {
49-
child.kill()
54+
try {
55+
child.kill()
56+
} catch {}
5057
})
5158

5259
child

test/onPort.test.js

+54
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,57 @@ test('--on-port flag', { skip: !hasAsyncHooks() }, (t) => {
5959
t.ok(regexp.test(line), 'line matches ' + regexp)
6060
})
6161
})
62+
63+
test('assume --on-port flag if -- node is set', { skip: !hasAsyncHooks() }, (t) => {
64+
const lines = [
65+
/Running 1s test @ .*$/,
66+
/10 connections.*$/,
67+
/$/,
68+
/.*/,
69+
/$/,
70+
/Stat.*2\.5%.*50%.*97\.5%.*99%.*Avg.*Stdev.*Max.*$/,
71+
/.*/,
72+
/Latency.*$/,
73+
/$/,
74+
/.*/,
75+
/Stat.*1%.*2\.5%.*50%.*97\.5%.*Avg.*Stdev.*Min.*$/,
76+
/.*/,
77+
/Req\/Sec.*$/,
78+
/$/,
79+
/Bytes\/Sec.*$/,
80+
/.*/,
81+
/$/,
82+
/Req\/Bytes counts sampled once per second.*$/,
83+
/# of samples: 10*$/,
84+
/$/,
85+
/.* requests in ([0-9]|\.)+s, .* read/
86+
]
87+
88+
t.plan(lines.length * 2)
89+
90+
const child = spawn(process.execPath, [
91+
path.join(__dirname, '..'),
92+
'-c', '10',
93+
'-d', '1',
94+
'/',
95+
'--', 'node', path.join(__dirname, './targetProcess')
96+
], {
97+
cwd: __dirname,
98+
env: process.env,
99+
stdio: ['ignore', 'pipe', 'pipe'],
100+
detached: false
101+
})
102+
103+
t.teardown(() => {
104+
child.kill()
105+
})
106+
107+
child
108+
.stderr
109+
.pipe(split())
110+
.on('data', (line) => {
111+
const regexp = lines.shift()
112+
t.ok(regexp, 'we are expecting this line')
113+
t.ok(regexp.test(line), 'line matches ' + regexp + `actual: ${line} expected: ${regexp}`)
114+
})
115+
})

0 commit comments

Comments
 (0)