diff --git a/.gitignore b/.gitignore index 5cbaec63..526c682a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ build/Release # Dependency directory node_modules +# Dependency lock +package-lock.json + # Optional npm cache directory .npm diff --git a/README.md b/README.md index cd215220..f5b25d31 100644 --- a/README.md +++ b/README.md @@ -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 `[]` 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`. diff --git a/lib/httpRequestBuilder.js b/lib/httpRequestBuilder.js index 8ccb589a..d075f5f9 100644 --- a/lib/httpRequestBuilder.js +++ b/lib/httpRequestBuilder.js @@ -13,6 +13,7 @@ function requestBuilder (defaults) { headers: {}, body: Buffer.alloc(0), hostname: 'localhost', + setupRequest: reqData => reqData, port: 80 } @@ -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 diff --git a/samples/requests-sample.js b/samples/requests-sample.js index 4b777196..7f447f39 100644 --- a/samples/requests-sample.js +++ b/samples/requests-sample.js @@ -37,7 +37,7 @@ 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' @@ -45,7 +45,11 @@ function startBench () { // 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) diff --git a/test/requestIterator.test.js b/test/requestIterator.test.js index 9e4c3259..dc3d59e8 100644 --- a/test/requestIterator.test.js +++ b/test/requestIterator.test.js @@ -187,3 +187,51 @@ test('request iterator should replace all [] tags with generated IDs when ca t.equal(result.includes('[]'), false, 'One or more [] 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') +})