mirror of
https://github.com/aykhans/sarin.git
synced 2026-02-27 22:39:13 +00:00
Add Lua and JavaScript scripting documentation
This commit is contained in:
15
README.md
15
README.md
@@ -22,13 +22,14 @@
|
|||||||
|
|
||||||
Sarin is designed for efficient HTTP load testing with minimal resource consumption. It prioritizes simplicity—features like templating add zero overhead when unused.
|
Sarin is designed for efficient HTTP load testing with minimal resource consumption. It prioritizes simplicity—features like templating add zero overhead when unused.
|
||||||
|
|
||||||
| ✅ Supported | ❌ Not Supported |
|
| ✅ Supported | ❌ Not Supported |
|
||||||
| ---------------------------------------------------------- | --------------------------------- |
|
| ---------------------------------------------------------- | ------------------------------- |
|
||||||
| High-performance with low memory footprint | Detailed response body analysis |
|
| High-performance with low memory footprint | Detailed response body analysis |
|
||||||
| Long-running duration/count based tests | Extensive response statistics |
|
| Long-running duration/count based tests | Extensive response statistics |
|
||||||
| Dynamic requests via 320+ template functions | Web UI or complex TUI |
|
| Dynamic requests via 320+ template functions | Web UI or complex TUI |
|
||||||
| Multiple proxy protocols<br>(HTTP, HTTPS, SOCKS5, SOCKS5H) | Scripting or multi-step scenarios |
|
| Request scripting with Lua and JavaScript | Distributed load testing |
|
||||||
| Flexible config (CLI, ENV, YAML) | HTTP/2, HTTP/3, WebSocket, gRPC |
|
| Multiple proxy protocols<br>(HTTP, HTTPS, SOCKS5, SOCKS5H) | HTTP/2, HTTP/3, WebSocket, gRPC |
|
||||||
|
| Flexible config (CLI, ENV, YAML) | Plugins / extensions ecosystem |
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ Use `-s` or `--show-config` to see the final merged configuration before sending
|
|||||||
| [Cookies](#cookies) | `cookies`<br>(object) | `-cookie` / `-C`<br>(string / []string) | `SARIN_COOKIE`<br>(string) | - | HTTP cookies |
|
| [Cookies](#cookies) | `cookies`<br>(object) | `-cookie` / `-C`<br>(string / []string) | `SARIN_COOKIE`<br>(string) | - | HTTP cookies |
|
||||||
| [Proxy](#proxy) | `proxy`<br>(string / []string) | `-proxy` / `-X`<br>(string / []string) | `SARIN_PROXY`<br>(string) | - | Proxy URL(s) |
|
| [Proxy](#proxy) | `proxy`<br>(string / []string) | `-proxy` / `-X`<br>(string / []string) | `SARIN_PROXY`<br>(string) | - | Proxy URL(s) |
|
||||||
| [Values](#values) | `values`<br>(string / []string) | `-values` / `-V`<br>(string / []string) | `SARIN_VALUES`<br>(string) | - | Template values (key=value) |
|
| [Values](#values) | `values`<br>(string / []string) | `-values` / `-V`<br>(string / []string) | `SARIN_VALUES`<br>(string) | - | Template values (key=value) |
|
||||||
|
| [Lua](#lua) | `lua`<br>(string / []string) | `-lua`<br>(string / []string) | `SARIN_LUA`<br>(string) | - | Lua script(s) |
|
||||||
|
| [Js](#js) | `js`<br>(string / []string) | `-js`<br>(string / []string) | `SARIN_JS`<br>(string) | - | JavaScript script(s) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -374,3 +376,133 @@ values: |
|
|||||||
```sh
|
```sh
|
||||||
SARIN_VALUES="key1=value1"
|
SARIN_VALUES="key1=value1"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Lua
|
||||||
|
|
||||||
|
Lua script(s) for request transformation. Each script must define a global `transform` function that receives a request object and returns the modified request object. Scripts run after template rendering, before the request is sent.
|
||||||
|
|
||||||
|
If multiple Lua scripts are provided, they are chained in order—the output of one becomes the input to the next. When both Lua and JavaScript scripts are specified, all Lua scripts run first, then all JavaScript scripts.
|
||||||
|
|
||||||
|
**Script sources:**
|
||||||
|
|
||||||
|
Scripts can be provided as:
|
||||||
|
|
||||||
|
- **Inline script:** Direct script code
|
||||||
|
- **File reference:** `@/path/to/script.lua` or `@./relative/path.lua`
|
||||||
|
- **URL reference:** `@http://...` or `@https://...`
|
||||||
|
- **Escaped `@`:** `@@...` for inline scripts that start with a literal `@`
|
||||||
|
|
||||||
|
**The `transform` function:**
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function transform(req)
|
||||||
|
-- req.method (string) - HTTP method (e.g. "GET", "POST")
|
||||||
|
-- req.path (string) - URL path (e.g. "/api/users")
|
||||||
|
-- req.body (string) - Request body
|
||||||
|
-- req.headers (table of string/arrays) - HTTP headers (e.g. {["X-Key"] = "value"})
|
||||||
|
-- req.params (table of string/arrays) - Query parameters (e.g. {["id"] = "123"})
|
||||||
|
-- req.cookies (table of string/arrays) - Cookies (e.g. {["session"] = "abc"})
|
||||||
|
|
||||||
|
req.headers["X-Custom"] = "my-value"
|
||||||
|
return req
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** Header, parameter, and cookie values can be a single string or a table (array) for multiple values per key (e.g. `{"val1", "val2"}`).
|
||||||
|
|
||||||
|
**YAML example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
lua: |
|
||||||
|
function transform(req)
|
||||||
|
req.headers["X-Custom"] = "my-value"
|
||||||
|
return req
|
||||||
|
end
|
||||||
|
|
||||||
|
# OR
|
||||||
|
|
||||||
|
lua:
|
||||||
|
- "@/path/to/script1.lua"
|
||||||
|
- "@/path/to/script2.lua"
|
||||||
|
```
|
||||||
|
|
||||||
|
**CLI example:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
-lua 'function transform(req) req.headers["X-Custom"] = "my-value" return req end'
|
||||||
|
|
||||||
|
# OR
|
||||||
|
|
||||||
|
-lua @/path/to/script1.lua -lua @/path/to/script2.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
**ENV example:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SARIN_LUA='function transform(req) req.headers["X-Custom"] = "my-value" return req end'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Js
|
||||||
|
|
||||||
|
JavaScript script(s) for request transformation. Each script must define a global `transform` function that receives a request object and returns the modified request object. Scripts run after template rendering, before the request is sent.
|
||||||
|
|
||||||
|
If multiple JavaScript scripts are provided, they are chained in order—the output of one becomes the input to the next. When both Lua and JavaScript scripts are specified, all Lua scripts run first, then all JavaScript scripts.
|
||||||
|
|
||||||
|
**Script sources:**
|
||||||
|
|
||||||
|
Scripts can be provided as:
|
||||||
|
|
||||||
|
- **Inline script:** Direct script code
|
||||||
|
- **File reference:** `@/path/to/script.js` or `@./relative/path.js`
|
||||||
|
- **URL reference:** `@http://...` or `@https://...`
|
||||||
|
- **Escaped `@`:** `@@...` for inline scripts that start with a literal `@`
|
||||||
|
|
||||||
|
**The `transform` function:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function transform(req) {
|
||||||
|
// req.method (string) - HTTP method (e.g. "GET", "POST")
|
||||||
|
// req.path (string) - URL path (e.g. "/api/users")
|
||||||
|
// req.body (string) - Request body
|
||||||
|
// req.headers (object of string/arrays) - HTTP headers (e.g. {"X-Key": "value"})
|
||||||
|
// req.params (object of string/arrays) - Query parameters (e.g. {"id": "123"})
|
||||||
|
// req.cookies (object of string/arrays) - Cookies (e.g. {"session": "abc"})
|
||||||
|
|
||||||
|
req.headers["X-Custom"] = "my-value";
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** Header, parameter, and cookie values can be a single string or an array for multiple values per key (e.g. `["val1", "val2"]`).
|
||||||
|
|
||||||
|
**YAML example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
js: |
|
||||||
|
function transform(req) {
|
||||||
|
req.headers["X-Custom"] = "my-value";
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
# OR
|
||||||
|
|
||||||
|
js:
|
||||||
|
- "@/path/to/script1.js"
|
||||||
|
- "@/path/to/script2.js"
|
||||||
|
```
|
||||||
|
|
||||||
|
**CLI example:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
-js 'function transform(req) { req.headers["X-Custom"] = "my-value"; return req; }'
|
||||||
|
|
||||||
|
# OR
|
||||||
|
|
||||||
|
-js @/path/to/script1.js -js @/path/to/script2.js
|
||||||
|
```
|
||||||
|
|
||||||
|
**ENV example:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SARIN_JS='function transform(req) { req.headers["X-Custom"] = "my-value"; return req; }'
|
||||||
|
```
|
||||||
|
|||||||
122
docs/examples.md
122
docs/examples.md
@@ -15,6 +15,7 @@ This guide provides practical examples for common Sarin use cases.
|
|||||||
- [Docker Usage](#docker-usage)
|
- [Docker Usage](#docker-usage)
|
||||||
- [Dry Run Mode](#dry-run-mode)
|
- [Dry Run Mode](#dry-run-mode)
|
||||||
- [Show Configuration](#show-configuration)
|
- [Show Configuration](#show-configuration)
|
||||||
|
- [Scripting](#scripting)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -894,3 +895,124 @@ headers:
|
|||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## Scripting
|
||||||
|
|
||||||
|
Transform requests using Lua or JavaScript scripts. Scripts run after template rendering, before the request is sent.
|
||||||
|
|
||||||
|
**Add a custom header with Lua:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com/api -r 1000 -c 10 \
|
||||||
|
-lua 'function transform(req) req.headers["X-Custom"] = "my-value" return req end'
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/api
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
lua: |
|
||||||
|
function transform(req)
|
||||||
|
req.headers["X-Custom"] = "my-value"
|
||||||
|
return req
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
**Modify request body with JavaScript:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com/api/data -r 1000 -c 10 \
|
||||||
|
-M POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-B '{"name": "test"}' \
|
||||||
|
-js 'function transform(req) { var body = JSON.parse(req.body); body.timestamp = Date.now(); req.body = JSON.stringify(body); return req; }'
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/api/data
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
body: '{"name": "test"}'
|
||||||
|
js: |
|
||||||
|
function transform(req) {
|
||||||
|
var body = JSON.parse(req.body);
|
||||||
|
body.timestamp = Date.now();
|
||||||
|
req.body = JSON.stringify(body);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
**Load script from a file:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com/api -r 1000 -c 10 \
|
||||||
|
-lua @./scripts/transform.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/api
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
lua: "@./scripts/transform.lua"
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
**Load script from a URL:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com/api -r 1000 -c 10 \
|
||||||
|
-js @https://example.com/scripts/transform.js
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/api
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
js: "@https://example.com/scripts/transform.js"
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
**Chain multiple scripts (Lua runs first, then JavaScript):**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com/api -r 1000 -c 10 \
|
||||||
|
-lua @./scripts/auth.lua \
|
||||||
|
-lua @./scripts/headers.lua \
|
||||||
|
-js @./scripts/body.js
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/api
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
lua:
|
||||||
|
- "@./scripts/auth.lua"
|
||||||
|
- "@./scripts/headers.lua"
|
||||||
|
js: "@./scripts/body.js"
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ Flags:
|
|||||||
-V, -values []string List of values for templating (e.g. "key1=value1")
|
-V, -values []string List of values for templating (e.g. "key1=value1")
|
||||||
-T, -timeout time Timeout for the request (e.g. 400ms, 3s, 1m10s) (default %v)
|
-T, -timeout time Timeout for the request (e.g. 400ms, 3s, 1m10s) (default %v)
|
||||||
-I, -insecure bool Skip SSL/TLS certificate verification (default %v)
|
-I, -insecure bool Skip SSL/TLS certificate verification (default %v)
|
||||||
-lua []string Lua script for request transformation (inline or @file/@url)
|
-lua []string Lua script for request transformation (inline or @file/@url)
|
||||||
-js []string JavaScript script for request transformation (inline or @file/@url)`
|
-js []string JavaScript script for request transformation (inline or @file/@url)`
|
||||||
|
|
||||||
var _ IParser = ConfigCLIParser{}
|
var _ IParser = ConfigCLIParser{}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user