Think curl, parallel an jq rolled into one tool.
phat is an HTTP client:
$ phat https://ze.api/users
[{"user": "bittrance"}, {"user": "harold"}]
$ echo '{"user": "rookie"}' | phat --method=POST https://ze.api/users
{"result": "success"}
phat is a load testing tool
$ phat --interval=10ms --jitter=5ms --report-interval=1m https://ze.api/users
{
"timestamp": "2020-01-10 17:02:00+01:00",
"total_calls": 59998,
"results": [
{
"status": 200,
"calls": 59758,
"ratio": 0.996,
"median_latency": 17.2,
"p95_latency": 40.9,
"p99_latency_ms": 80.1,
max_latency_ms=250
},
{
"status": 501,
"calls": 240,
"ration": 0.004,
"median_latency": 17.2
"p95_latency": 40.9,
"p99_latency_ms": 80.1,
"max_latency_ms": 250
}
]
}
phat is a REST API verification tool.
$ phat --verify-body='.user == "bittrance"' https://ze.api/users/harold
body '{"user": "harold"}' does not match '.user == "bittrance"'
phat can transform HTTP results and create streams from arrays:
$ phat --transform-body '.[]' https://ze.api/users
{"user": "bittrance"}
{"user": "harold"}
phat is an HTTP streaming tool
$ phat --transform-body '.[]' https://ze.api/users | \
phat --smaple '0.01' --body '{"username": ${body.user}, "role": "admin"}' https://other.api/users/new
phat can be invoked as a cli tool to construct simple pipelines where each operator occur at most once, in a fixed order.
- sequence | file
- shaping
- request
- filter
- verify
- transform
Operators are described below. See above for example invocations. By default, phat will exit non-zero if any response event with status >= 400 (or marked as invalid by the verify operator) reaches the end of the pipeline.
It is also possible to invoke phat as a script engine with a YAML-based scripting language which adheres closely to the cli options. The CLI-based invocation works well for ad-hoc use cases, but when used in a formal role, e.g. to automate testing, it is desirable to have an invocation format more amenable to version control.
This format is also able create pipelines from arbitrary sequences of operators, even sequences that are not possible with cli options. This is particularly pertinent when chaining requests (e.g. reading entities from one call and using the resulting events to make further calls) since the shell pipe is likely to be a bottleneck.
Translating the streaming example from above into YAML will yield significant performance improvement for a large dataset:
pipeline:
- operator: request
url: https://ze.api/users
- operator: shaping
sample: 0.01
- operator: transform
body: '.[]'
- operator: request
method: post
url: https://other.api/users/new
body:
username: ${body.user}
role: admin
At heart, phat is a collection of operators which can be strung together to form a pipeline. Operators have an input event type and an output event type. The main ones are Request which is a set of HTTP headers and a request body, and Response which is an HTTP answer, with a status code, a set of HTTP headers, metrics and a response body. A Response event will be automatically converted into a Request event as necessary by discarding its status and headers.
The sequence operator generates a stream of input events with JSON {"seq": <int>}
with a sequence number starting with zero. The sequence operator is a source and will conflict with other source operators.
--sequence
The file operator reads a byte stream from file or stdin and emits a stream of "input" events. The file operator is a source and will conflict with other source operators.
--file=<ndjson file>
The shaping operator provides various ways to sample the stream of input requests. Unlike other operators, the shaping operator has an internal queue and may not immediately propagate back pressure.
--sample=0.00-1
--count=<integer>
--interval=<float>
--jitter=<float>
This operator performs one HTTP request for each input entry, up to a configurable maximum number of concurrent requests.
--max-concurrent=<integer>
<url>
--body=<template>
--header=<template>
--method={GET|POST|PUT|HEAD|DELETE|PATCH}
The request operator also provides a way to perform dry-runs. If any of the mock options are included, the request operator will not perform an actual HTTP request, but will instead generate an response event from the data provided by those mock options.
--mock-status=<HTTP status code>
--mock-header=<template>
--mock-body=<template>
The filter operator can be used to discard response events if they match various criteria. Non-matching events will be let through.
--filter-status=<HTTP status code>
--filter-header=
--filter-body=<JMESPath>
The verify operator will mark an event as invalid if any of its options does not match that event.
--verify-status=<HTTP status code>
--verify-header=
--verify-body=<JMESPath>
The annotate operator creates a new response event which inherits status, headers and metrics from the source event, and whose body is effectively a serialized version of the source response event. This is useful to produce raw data for further statistical analysis.
The report operator collects response events and periodically emits a new event whose body produces a statistical summary of the collected responses. It can also be instructed to send the summaries somewhere else (e.g. stderr) in which case it will propagate the original response events untouched.
--report-interval=<float>
--report-output=<file>
The transform operator can be used to transform the JSON body of a response event using a JMES expression. Note that the request operator can use templating to extract parts of a JSON body from a previous step in the pipeline, so the transform operator is primarily useful for transforming the body at the end of the pipeline.
--transform-body=<JMESPath>