endpoints section
endpoints:
- [declare: declare_subsection]
[headers: headers]
[body: body]
[load_pattern: load_pattern_subsection]
[method: method]
[peak_load: peak_load]
[tags: tags]
url: template
[provides: provides_subsection]
[on_demand: boolean]
[logs: logs_subsection]
[max_parallel_requests: unsigned integer]
[no_auto_returns: boolean]
[request_timeout: duration]
The endpoints section declares what HTTP endpoints will be called during a test.
-
declareOptional - See the declare subsection -
headersOptional - See headers -
bodyOptional - See the body subsection -
load_patternOptional - See the load_pattern section -
methodOptional - A string representation for a valid HTTP method verb. Defaults toGET -
peak_loadOptional* - A template representing what the "peak load" for this endpoint should be. The term "peak load" represents how much traffic is generated for this endpoint when the load_pattern reaches100%. Aload_patterncan go higher than100%, so aload_patternof200%, for example, would mean it would go double the definedpeak_load. Only variables defined in the vars section can be interpolated.* While
peak_loadis marked as optional that is only true if the current endpoint has a provides_subsection, and in that case this endpoint is called only as frequently as needed to keep the buffers of the providers it feeds full.A valid
load_patternis a number--integer or decimal--followed by an optional space and the string "hpm" (meaning "hits per minute") or "hps" (meaning "hits per second").Examples:
50hpm- 50 hits per minute300 hps- 300 hits per second -
tagsOptional - Key/value string/template pairs.Tags are a series of key/value pairs used to distinguish each endpoint. Tags can be used to include certain endpoints in a
tryrun, and also make it possible for a single endpoint to have its results statistics aggregated in multiple groups. Because tag values are templates only tags which can be resolved statically at the beginning of a test can be used with theincludeflag of atryrun. A reference to a provider can cause a single endpoint to have multiple groups of tags. Each one of these groups will have its own statistics in the results. For example if an endpoint had the following tags:tags: name: Subscribe status: ${response.status}A new group of aggregated stats will be created for every status code returned by the endpoint.
All endpoints have the following implicitly defined tags:
Name Description methodThe HTTP method for the endpoint. urlThe endpoint's url with any dynamic pieces being replaced with an asterisk. _idThe index of this endpoint in the list of endpoints, starting with 0. Of the implicitly defined tags only
urlcan be overwritten which is helpful in cases such as when an entire url is dynamically generated and it would otherwise show up as*. -
url- A template specifying the fully qualified url to the endpoint which will be requested. -
providesOptional - See the provides subsection -
on_demandOptional - A boolean which indicates that this endpoint should only be called when another endpoint first needs data that this endpoint provides. If the endpoint has noprovidesit has no affect. -
logsOptional - See the logs subsection -
max_parallel_requestsOptional - Limits how many requests can be "open" at any point for the endpoint. WARNING: this can cause coordinated omission, invalidating the test statistics. -
no_auto_returnsOptional - A boolean which indicates that anyauto_returnproviders referenced within this endpoint will haveauto_returndisabled--meaning values pulled from those providers will not be automatically pushed back to the provider after a response is received. Defaults tofalse. -
request_timeoutOptional - A duration signifying how long a request will wait for a response before it times out. When not specified, the value from the client config will be used.
Using providers to build a request
Providers can be referenced anywhere templates can be used and also in the declare subsection.
body subsection
body: template
body: file: template
body:
multipart:
field_name:
[headers: headers]
body: template
field_name:
[headers: headers]
body:
file: template
A request body can be in one of three formats: a template to send a string as the body, a file which will send the contents of a file as the body, or a multipart body.
To send the contents of a file the body parameter should be an object with a single key of file and the value being a template. Relative paths resolve relative to the config file used to execute pewpew.
To send a multipart body, the body parameter should be an object with a single key of multipart and the value being an object of key/value pairs, where each key/value pair represents a piece of the multipart body. The keys represent the field_names used in an HTML form and the values are objects with the following properties:
headersOptional - Headers that will be included with this piece of the multipart body. For example, it is not uncommon to include acontent-typeheader with a piece of a multipart body which includes a file.body- Either a template which will send a string value or an object with a single key offileand the value being a template--which will send the contents of a file.
When a multipart body is used for an endpoint each request will have the content-type header added with the value multipart/form-data and the necessary boundary. If there is already a content-type header set for the request it will be overwritten unless it is starts with multipart/--then the necessary boundary will be appended. If a multipart/... content-type is manually set with the request, make sure to not include a boundary parameter.
For any request which has a content-type of multipart/form-data, a Content-Disposition header will be added to each piece in the multipart body with a value of form-data; name="field_name" (where field_name is substituted with the piece's field_name). If a Content-Disposition header is explicitly specified for a piece it will not be overwritten.
File example:
body:
file: a_file.txt
Multipart example:
body:
multipart:
foo:
headers:
Content-Type: image/jpeg
body:
file: foo.jpg
bar:
body: some text
declare subsection
declare: name: expression
A declare_subsection provides the ability to select multiple values from a single provider. Without using a declare_subsection, multiple references to a provider will only select a single value. For example, in:
endpoints:
- method: PUT
url: https://localhost/ship/${shipId}/speed
body: '{"shipId":"${shipId}","kesselRunTime":75}'
both references to the provider shipId will resolve to the same value, which in many cases is desired.
The declare_subsection is in the format of key/value pairs where the value is an expression. Every key can function as a provider and can be interpolated just as a provider would be.
Example 1
endpoints:
- declare:
shipIds: collect(shipId, 3, 5)
method: DELETE
url: https://localhost/ships
body: '{"shipIds":${shipIds}}'
Calls the endpoint DELETE /ships where the body is interpolated with an array of ship ids. shipIds will have a length between three and five.
Example 2
endpoints:
- declare:
destroyedShipId: shipId
method: PUT
url: https://localhost/ship/${shipId}/destroys/${destroyedShipId}
Calls PUT on an endpoint where shipId and destroyedShipId are interpolated to different values.
provides subsection
provides:
provider_name:
select: select
[for_each: for_each]
[where: expression]
[send: block | force | if_not_full]
The provides_subsection is how data can be sent to a provider from an HTTP response. provider_name is a reference to a provider which must be declared in the root providers section. For every HTTP response that is received, zero or more values can be sent to the provider based upon the conditions specified.
Sending data to a provider is done with a SQL-like syntax. The select, for_each and where sections use expressions to reference providers in addition to the special variables "request", "response" and "stats". "request" provides a means of accessing data that was sent with the request, "response" provides a means of accessing data returned with the response and "stats" give access to measurements about the request (currently only rtt meaning round-trip time).
The request object has the properties start-line, method, url, headers, headers_all and body which provide access to the respective sections in the HTTP request. Similarly, the response object has the properties start-line, headers, headers_all and body in addition to status which indicates the HTTP response status code. See this MDN article on HTTP messages for more details on the structure of HTTP requests and responses.
start-line is a string and headers is represented as a JSON object with key/value string pairs. In the event where a request or response has multiple headers with the same name, the headers_all property can be used which is a JSON object where the header name is the key and the value an array of header values. Currently, body in the request is always a string and body in the response is parsed as a JSON value, when possible, otherwise it is a string. status is a number. method is a string and url is an object with the same properties as the web URL object (see this MDN article).
-
select- Determines the shape of the data sent to the provider.selectis interpreted as a JSON object where any string value is evaluated as an expression. -
for_eachOptional - Evaluatesselectfor each element in an array or arrays. This is specified as an array of expressions. Expressions can evaluate to any JSON data type, but those which evaluate to an array will have each of their elements iterated over andselectis evaluated for each. When multiple expressions evaluate to an array then the cartesian product of the arrays is produced.The
selectandwhereparameters can access the elements provided byfor_eachthrough the valuefor_eachjust like accessing a value from a provider. Because afor_eachcan iterate over multiple arrays, each element can be accessed by indexing into the array. For examplefor_each[1]would access the element from the second array (indexes are referenced with zero based counting so0represents the element in the first array). -
whereOptional - Allows conditionally sending data to a provider based on a predicate. This is an expression which evaluates to a boolean value, indicating whetherselectshould be evaluated for the current data set. -
sendOptional - Specify the behavior that should be used when sending data to a provider. Valid options for this parameter areblock,force, andif_not_full. Defaults toif_not_fullif the endpoint has apeak_loadotherwiseblock.blockindicates that if the provider's buffer is full, further endpoint calls will be blocked until there's room in the provider's buffer for the value. If an endpoint has multiple provides which areblock, then the blocking will only wait for at least one of the providers' buffers to have room.forceindicates that the value will be sent to the provider regardless of whether its buffer is "full". This can make a provider's buffer exceed its soft limit.if_not_fullindicates that the value will be sent to the provider only if the provider is not full.
Example 1
With an HTTP response with the following body
{ "session": "abc123" }
and a provides section defined as:
provides:
session:
select: response.body.session
where: response.status < 400
the session provider would be given the value "abc123" if the status code was less than 400 otherwise nothing would be sent to the session provider.
Example 2
With an HTTP response with the following body:
{
"characters": [
{
"type": "Human",
"id": "1000",
"name": "Luke Skywalker",
"friends": ["1002", "1003", "2000", "2001"],
"appearsIn": [4, 5, 6],
"homePlanet": "Tatooine",
},
{
"type": "Human",
"id": "1001",
"name": "Darth Vader",
"friends": ["1004"],
"appearsIn": [4, 5, 6],
"homePlanet": "Tatooine",
},
{
"type": "Droid",
"id": "2001",
"name": "R2-D2",
"friends": ["1000", "1002", "1003"],
"appearsIn": [4, 5, 6],
"primaryFunction": "Astromech",
}
]
}
and our provides section is defined as:
provides:
names:
select:
name: for_each[0].name
for_each:
- response.body.characters
The names provider would be sent the following values: { "name": "Luke Skywalker" }, { "name": "Darth Vader" }, { "name": "R2-D2" }.
Example 3
It is also possible to access the length of an array by accessing the length property.
Using the same response data from example 2, with a provides section defined as:
provides:
friendsCount:
select:
id: for_each[0].id
count: for_each[0].friends.length
for_each:
- response.body.characters
The friendsCount provider would be sent the following values: { "id": 1000, "count": 4 }, { "id": 1001, "count": 1 }, { "id": 2001, "count": 3 }.
logs subsection
logs:
logger_name:
select: select
[for_each: for_each]
[where: expression]
The logs_subsection provides a means of sending data to a logger based on the result of an HTTP response. logger_name is a reference to a logger which must be declared in the root loggers section. It is structured in the same way as the provides_subsection except there is no explicit send parameter. When data is sent to a logger it has the same behavior as send: block, which means logging data can potentially block further requests from happening if a logger were to get "backed up". This is unlikely to be a problem unless a large amount of data was consistently logged. It is also possible to log to the same logger multiple times in a single endpoint by repeating the logger_name with a new select.
select- Determines the shape of the data sent into the logger.for_eachOptional - Evaluatesselectfor each element in an array or arrays.whereOptional - Allows conditionally sending data into a logger based on a predicate.