Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add setupRequest per request for requests array #239

Merged
merged 2 commits into from Jan 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -26,6 +26,9 @@ build/Release
# Dependency directory
node_modules

# Dependency lock
package-lock.json

# Optional npm cache directory
.npm

Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -233,7 +233,7 @@ Start autocannon against the given target.
* `connectionRate`: A `Number` stating the rate of requests to make per second from each individual connection. No rate limiting by default. _OPTIONAL_
* `overallRate`: A `Number` stating the rate of requests to make per second from all connections. `connectionRate` takes precedence if both are set. No rate limiting by default. _OPTIONAL_
* `reconnectRate`: A `Number` which makes the individual connections disconnect and reconnect to the server whenever it has sent that number of requests. _OPTIONAL_
* `requests`: An `Array` of `Object`s which represents the sequence of requests to make while benchmarking. Can be used in conjunction with the `body`, `headers` and `method` params above. The `Object`s in this array can have `body`, `headers`, `method`, or `path` attributes, which overwrite those that are passed in this `opts` object. Therefore, the ones in this (`opts`) object take precedence and should be viewed as defaults. Check the samples folder for an example of how this might be used. _OPTIONAL_.
* `requests`: An `Array` of `Object`s which represents the sequence of requests to make while benchmarking. Can be used in conjunction with the `body`, `headers` and `method` params above. The `Object`s in this array can have `body`, `headers`, `method`, or `path` attributes, which overwrite those that are passed in this `opts` object. Therefore, the ones in this (`opts`) object take precedence and should be viewed as defaults. Additionally, an optional `setupRequest` `Function` may be provided that will mutate the raw `request` object, e.g. `request.method = 'GET'`. Check the samples folder for an example of how this might be used. _OPTIONAL_.
* `idReplacement`: A `Boolean` which enables the replacement of `[<id>]` tags within the request body with a randomly generated ID, allowing for unique fields to be sent with requests. Check out [an example of programmatic usage](./samples/using-id-replacement.js) can be found in the samples. _OPTIONAL_ default: `false`
* `forever`: A `Boolean` which allows you to setup an instance of autocannon that restarts indefinitely after emiting results with the `done` event. Useful for efficiently restarting your instance. To stop running forever, you must cause a `SIGINT` or call the `.stop()` function on your instance. _OPTIONAL_ default: `false`
* `servername`: A `String` identifying the server name for the SNI (Server Name Indication) TLS extension. _OPTIONAL_ default: `undefined`.
Expand Down
4 changes: 4 additions & 0 deletions lib/httpRequestBuilder.js
Expand Up @@ -13,6 +13,7 @@ function requestBuilder (defaults) {
headers: {},
body: Buffer.alloc(0),
hostname: 'localhost',
setupRequest: reqData => reqData,
port: 80
}

Expand All @@ -27,6 +28,9 @@ function requestBuilder (defaults) {
reqData.headers = Object.assign({}, defaults.headers, reqData.headers)

reqData = Object.assign({}, defaults, reqData)

reqData = reqData.setupRequest(reqData)

// for some reason some tests fail with method === undefined
// the reqData.method should be set to SOMETHING in this case
// cannot find reason for failure if `|| 'GET'` is taken out
Expand Down
8 changes: 6 additions & 2 deletions samples/requests-sample.js
Expand Up @@ -37,15 +37,19 @@ function startBench () {
// this will automatically add the pregenerated auth token
},
{
method: 'PUT', // this should be a put for modifying secret details
method: 'GET', // this should be a put for modifying secret details
path: '/mySecretDetails',
headers: { // let submit some json?
'Content-type': 'application/json; charset=utf-8'
},
// we need to stringify the json first
body: JSON.stringify({
name: 'my new name'
})
}),
setupRequest: reqData => {
reqData.method = 'PUT' // we are overriding the method 'GET' to 'PUT' here
return reqData
}
}
]
}, finishedBench)
Expand Down
48 changes: 48 additions & 0 deletions test/requestIterator.test.js
Expand Up @@ -187,3 +187,51 @@ test('request iterator should replace all [<id>] tags with generated IDs when ca
t.equal(result.includes('[<id>]'), false, 'One or more [<id>] tags were not replaced')
t.equal(result.slice(-1), '0', 'Generated ID should end with request number')
})

test('request iterator should properly mutate requests if a setupRequest function is located', (t) => {
t.plan(6)

const opts = server.address()
opts.method = 'POST'

let i = 0

const requests1 = [
{
body: 'hello world',
setupRequest: req => {
req.body += i++
return req
}
},
{
method: 'POST',
body: 'modified',
setupRequest: req => {
req.method = 'GET'
return req
}
}
]

const request1Res = Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nContent-Length: 12\r\n\r\nhello world0`)
const request2Res = Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nContent-Length: 9\r\n\r\nmodified1`)
const request3Res = Buffer.from(`GET / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nContent-Length: 8\r\n\r\nmodified`)
const request4Res = Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\nheader: modifiedHeader\r\nContent-Length: 9\r\n\r\nmodified2`)
const request5Res = Buffer.from(`POST / HTTP/1.1\r\nHost: localhost:${server.address().port}\r\nConnection: keep-alive\r\n\r\n`)

opts.requests = requests1

const 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')
iterator.nextRequest() // verify it didn't affect the other request
t.same(iterator.currentRequest.requestBuffer, request3Res, 'request was okay')
iterator.nextRequest()
t.same(iterator.currentRequest.requestBuffer, request2Res, 'request was okay')
iterator.setHeaders({ header: 'modifiedHeader' })
t.same(iterator.currentRequest.requestBuffer, request4Res, 'request was okay')
iterator.setRequest() // this should build default request
t.same(iterator.currentRequest.requestBuffer, request5Res, 'request was okay')
})