Repository: mcollina/autocannon
Pull request #206 (2019-06-17 16:20:50)

Fix autocannon running in parallel when using setupClient issue #194


Patch:
diff --git a/.gitignore b/.gitignore
index 4c2dbe8..5cbaec6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,8 @@ node_modules
 # Optional npm cache directory
 .npm
 
+.idea
+
 # Optional REPL history
 .node_repl_history
 
diff --git a/lib/httpClient.js b/lib/httpClient.js
index 2579375..c50b728 100644
--- a/lib/httpClient.js
+++ b/lib/httpClient.js
@@ -7,37 +7,43 @@ const tls = require('tls')
 const retimer = require('retimer')
 const HTTPParser = require('http-parser-js').HTTPParser
 const RequestIterator = require('./requestIterator')
+const clone = require('clone')
 
 function Client (opts) {
   if (!(this instanceof Client)) {
     return new Client(opts)
   }
-  opts.setupClient = opts.setupClient || noop
-  opts.pipelining = opts.pipelining || 1
-  opts.port = opts.port || 80
-
-  this.opts = opts
-  this.timeout = (opts.timeout || 10) * 1000
-  this.ipc = !!opts.socketPath
-  this.secure = opts.protocol === 'https:'
-  if (this.secure && this.opts.port === 80) this.opts.port = 443
+
+  this.opts = clone(opts)
+
+  this.opts.setupClient = this.opts.setupClient || noop
+  this.opts.pipelining = this.opts.pipelining || 1
+  this.opts.port = this.opts.port || 80
+  this.timeout = (this.opts.timeout || 10) * 1000
+  this.ipc = !!this.opts.socketPath
+  this.secure = this.opts.protocol === 'https:'
+
+  if (this.secure && this.opts.port === 80) {
+    this.opts.port = 443
+  }
+
   this.parser = new HTTPParser(HTTPParser.RESPONSE)
-  this.requestIterator = new RequestIterator(opts.requests, opts)
+  this.requestIterator = new RequestIterator(this.opts)
 
   this.reqsMade = 0
 
   // used for request limiting
-  this.responseMax = opts.responseMax
+  this.responseMax = this.opts.responseMax
 
   // used for rate limiting
   this.reqsMadeThisSecond = 0
-  this.rate = opts.rate
+  this.rate = this.opts.rate
 
   // used for forcing reconnects
-  this.reconnectRate = opts.reconnectRate
+  this.reconnectRate = this.opts.reconnectRate
 
-  this.resData = new Array(opts.pipelining)
-  for (var i = 0; i < this.resData.length; i++) {
+  this.resData = new Array(this.opts.pipelining)
+  for (let i = 0; i < this.resData.length; i++) {
     this.resData[i] = {
       bytes: 0,
       headers: {},
@@ -49,7 +55,7 @@ function Client (opts) {
   this.cer = 0
   this.destroyed = false
 
-  opts.setupClient(this)
+  this.opts.setupClient(this)
 
   const handleTimeout = () => {
     // all pipelined requests have timed out here
diff --git a/lib/requestIterator.js b/lib/requestIterator.js
index cb8109b..aa17435 100644
--- a/lib/requestIterator.js
+++ b/lib/requestIterator.js
@@ -4,14 +4,14 @@ const hyperid = require('hyperid')(true)
 const inherits = require('util').inherits
 const requestBuilder = require('./httpRequestBuilder')
 
-function RequestIterator (requests, defaults) {
+function RequestIterator (opts) {
   if (!(this instanceof RequestIterator)) {
-    return new RequestIterator(requests, defaults)
+    return new RequestIterator(opts)
   }
 
-  this.reqDefaults = defaults
-  this.requestBuilder = requestBuilder(defaults)
-  this.setRequests(requests)
+  this.reqDefaults = opts
+  this.requestBuilder = requestBuilder(opts)
+  this.setRequests(opts.requests)
 }
 
 inherits(RequestIterator, Object)
diff --git a/package.json b/package.json
index 55d7f32..8604a3b 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
   "dependencies": {
     "chalk": "^2.4.2",
     "cli-table3": "^0.5.1",
+    "clone": "^2.1.2",
     "color-support": "^1.1.1",
     "cross-argv": "^1.0.0",
     "has-async-hooks": "^1.0.0",


Test patch:
diff --git a/test/httpClient.test.js b/test/httpClient.test.js
index 03c353a..efcffc3 100644
--- a/test/httpClient.test.js
+++ b/test/httpClient.test.js
@@ -98,6 +98,34 @@ test('http client automatically reconnects', (t) => {
   })
 })
 
+test('http clients should have a different body', (t) => {
+  t.plan(3)
+
+  let clientCnt = 0
+  const clients = []
+  const reqArray = ['John', 'Gabriel', 'Jason']
+  const opts = server.address()
+
+  opts.setupClient = (client) => {
+    client.setBody(JSON.stringify({ name: reqArray[clientCnt] }))
+    clientCnt++
+  }
+
+  for (let i = 0; i < 3; i++) {
+    const client = new Client(opts)
+
+    clients.push(client)
+  }
+
+  for (let i = 0; i < clients.length; i++) {
+    const client = clients[i]
+    const body = JSON.parse(client.requestIterator.currentRequest.body)
+
+    t.equal(body.name, reqArray[i], 'body match')
+    client.destroy()
+  }
+})
+
 test('client supports custom headers', (t) => {
   t.plan(3)
 
diff --git a/test/requestIterator.test.js b/test/requestIterator.test.js
index e24a652..7584419 100644
--- a/test/requestIterator.test.js
+++ b/test/requestIterator.test.js
@@ -10,19 +10,23 @@ test('request iterator should create requests with sensible defaults', (t) => {
 
   const opts = server.address()
 
-  let iterator = new RequestIterator(undefined, opts)
+  let iterator = new RequestIterator(opts)
 
   t.same(iterator.currentRequest.requestBuffer,
     Buffer.from(`GET / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\n\r\n`),
     'request is okay')
 
-  iterator = new RequestIterator([{}], opts)
+  opts.requests = [{}]
+
+  iterator = new RequestIterator(opts)
 
   t.same(iterator.currentRequest.requestBuffer,
     Buffer.from(`GET / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\n\r\n`),
     'request is okay')
 
-  iterator = new RequestIterator([], opts)
+  opts.requests = []
+
+  iterator = new RequestIterator(opts)
 
   t.notOk(iterator.currentRequest, 'request doesn\'t exist')
 })
@@ -33,7 +37,7 @@ test('request iterator should create requests with overwritten defaults', (t) =>
   const opts = server.address()
   opts.method = 'POST'
 
-  let iterator = new RequestIterator(undefined, opts)
+  let iterator = new RequestIterator(opts)
 
   t.same(iterator.currentRequest.requestBuffer,
     Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\n\r\n`),
@@ -59,7 +63,9 @@ test('request iterator should create requests with overwritten defaults', (t) =>
   const request1Res = Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhello world\r\n`)
   const request2Res = Buffer.from(`GET / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nContent-Length: 8\r\n\r\nmodified\r\n`)
 
-  let iterator = new RequestIterator(requests, opts)
+  opts.requests = requests
+
+  let iterator = new RequestIterator(opts)
 
   t.same(iterator.currentRequest.requestBuffer, request1Res, 'request was okay')
   iterator.nextRequest()
@@ -94,7 +100,9 @@ test('request iterator should allow for overwriting the requests passed in, but
   const request2Res = Buffer.from(`GET / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nContent-Length: 8\r\n\r\nmodified\r\n`)
   const request3Res = Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhell0 w0rld\r\n`)
 
-  let iterator = new RequestIterator(requests1, opts)
+  opts.requests = requests1
+
+  let iterator = new RequestIterator(opts)
 
   t.same(iterator.currentRequest.requestBuffer, request1Res, 'request was okay')
   iterator.nextRequest()
@@ -130,7 +138,9 @@ test('request iterator should allow for rebuilding the current request', (t) =>
   const request4Res = Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nheader: modifiedHeader\r\nContent-Length: 8\r\n\r\nmodified\r\n`)
   const request5Res = Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\n\r\n`)
 
-  let iterator = new RequestIterator(requests1, opts)
+  opts.requests = requests1
+
+  let iterator = new RequestIterator(opts)
   t.same(iterator.currentRequest.requestBuffer, request1Res, 'request was okay')
   iterator.setBody('modified')
   t.same(iterator.currentRequest.requestBuffer, request2Res, 'request was okay')
@@ -152,7 +162,7 @@ test('request iterator should not replace any [<id>] tags with generated IDs whe
   opts.body = '[<id>]'
   opts.requests = [{}]
 
-  const iterator = new RequestIterator(opts.requests, opts)
+  const iterator = new RequestIterator(opts)
   const result = iterator.move().toString().trim()
 
   const contentLength = result.split('Content-Length: ')[1].slice(0, 1)
@@ -171,7 +181,7 @@ test('request iterator should replace all [<id>] tags with generated IDs when ca
   opts.requests = [{}]
   opts.idReplacement = true
 
-  const iterator = new RequestIterator(opts.requests, opts)
+  const iterator = new RequestIterator(opts)
   const result = iterator.move().toString().trim()
 
   t.equal(result.includes('[<id>]'), false, 'One or more [<id>] tags were not replaced')