mirror of
https://github.com/aykhans/sarin.git
synced 2026-01-13 20:11:21 +00:00
Compare commits
5 Commits
46c6fa9912
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a512f3605d | |||
|
|
635c33008b | ||
| 3f2147ec6c | |||
| 92d0c5e003 | |||
| 27bc8f2e96 |
26
README.md
26
README.md
@@ -19,11 +19,11 @@
|
|||||||
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 (HTTP/HTTPS/SOCKS5/SOCKS5H) | Scripting or multi-step scenarios |
|
| Multiple proxy protocols<br>(HTTP, HTTPS, SOCKS5, SOCKS5H) | Scripting or multi-step scenarios |
|
||||||
| Flexible config (CLI, ENV, YAML) | HTTP/2, HTTP/3, WebSocket, gRPC |
|
| Flexible config (CLI, ENV, YAML) | HTTP/2, HTTP/3, WebSocket, gRPC |
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -74,18 +74,6 @@ Send 10,000 GET requests with 50 concurrent connections and a random User-Agent
|
|||||||
sarin -U http://example.com -r 10_000 -c 50 -H "User-Agent: {{ fakeit_UserAgent }}"
|
sarin -U http://example.com -r 10_000 -c 50 -H "User-Agent: {{ fakeit_UserAgent }}"
|
||||||
```
|
```
|
||||||
|
|
||||||
Example output:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────┬───────┬──────────┬───────────┬─────────┬───────────┬──────────┬───────────┐
|
|
||||||
│ Response │ Count │ Min │ Max │ Average │ P90 │ P95 │ P99 │
|
|
||||||
├──────────┼───────┼──────────┼───────────┼─────────┼───────────┼──────────┼───────────┤
|
|
||||||
│ 200 │ 10000 │ 78.038ms │ 288.153ms │ 94.71ms │ 103.078ms │ 131.08ms │ 269.218ms │
|
|
||||||
├──────────┼───────┼──────────┼───────────┼─────────┼───────────┼──────────┼───────────┤
|
|
||||||
│ Total │ 10000 │ 78.038ms │ 288.153ms │ 94.71ms │ 103.078ms │ 131.08ms │ 269.218ms │
|
|
||||||
└──────────┴───────┴──────────┴───────────┴─────────┴───────────┴──────────┴───────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
Run a 5-minute duration-based test:
|
Run a 5-minute duration-based test:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -112,15 +100,15 @@ For detailed documentation on all configuration options (URL, method, timeout, c
|
|||||||
|
|
||||||
## Templating
|
## Templating
|
||||||
|
|
||||||
Sarin supports Go templates in methods, bodies, headers, params, cookies, and values. Use the 320+ built-in functions to generate dynamic data for each request.
|
Sarin supports Go templates in URL paths, methods, bodies, headers, params, cookies, and values. Use the 320+ built-in functions to generate dynamic data for each request.
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sarin -U http://example.com/users \
|
sarin -U "http://example.com/users/{{ fakeit_UUID }}" -r 1000 -c 10 \
|
||||||
-V "ID={{ fakeit_UUID }}" \
|
-V "RequestID={{ fakeit_UUID }}" \
|
||||||
-H "X-Request-ID: {{ .Values.ID }}" \
|
-H "X-Request-ID: {{ .Values.RequestID }}" \
|
||||||
-B '{"id": "{{ .Values.ID }}"}'
|
-B '{"request_id": "{{ .Values.RequestID }}"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
For the complete templating guide and functions reference, see the **[Templating Guide](docs/templating.md)**.
|
For the complete templating guide and functions reference, see the **[Templating Guide](docs/templating.md)**.
|
||||||
|
|||||||
@@ -76,7 +76,21 @@ config3.yaml > config2.yaml > config4.yaml > config1.yaml
|
|||||||
|
|
||||||
## URL
|
## URL
|
||||||
|
|
||||||
Target URL. Must be HTTP or HTTPS.
|
Target URL. Must be HTTP or HTTPS. The URL path supports [templating](templating.md), allowing dynamic path generation per request.
|
||||||
|
|
||||||
|
> **Note:** Templating is only supported in the URL path. Host and scheme must be static.
|
||||||
|
|
||||||
|
**Example with dynamic path:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/users/{{ fakeit_UUID }}/profile
|
||||||
|
```
|
||||||
|
|
||||||
|
**CLI example with dynamic path:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U "http://example.com/users/{{ fakeit_UUID }}" -r 1000 -c 10
|
||||||
|
```
|
||||||
|
|
||||||
## Method
|
## Method
|
||||||
|
|
||||||
|
|||||||
234
docs/examples.md
234
docs/examples.md
@@ -6,8 +6,8 @@ This guide provides practical examples for common Sarin use cases.
|
|||||||
|
|
||||||
- [Basic Usage](#basic-usage)
|
- [Basic Usage](#basic-usage)
|
||||||
- [Request-Based vs Duration-Based Tests](#request-based-vs-duration-based-tests)
|
- [Request-Based vs Duration-Based Tests](#request-based-vs-duration-based-tests)
|
||||||
- [Dynamic Requests with Templating](#dynamic-requests-with-templating)
|
|
||||||
- [Headers, Cookies, and Parameters](#headers-cookies-and-parameters)
|
- [Headers, Cookies, and Parameters](#headers-cookies-and-parameters)
|
||||||
|
- [Dynamic Requests with Templating](#dynamic-requests-with-templating)
|
||||||
- [Request Bodies](#request-bodies)
|
- [Request Bodies](#request-bodies)
|
||||||
- [Using Proxies](#using-proxies)
|
- [Using Proxies](#using-proxies)
|
||||||
- [Output Formats](#output-formats)
|
- [Output Formats](#output-formats)
|
||||||
@@ -108,104 +108,6 @@ concurrency: 100
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Dynamic Requests with Templating
|
|
||||||
|
|
||||||
Generate a random User-Agent for each request:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sarin -U http://example.com -r 1000 -c 10 \
|
|
||||||
-H "User-Agent: {{ fakeit_UserAgent }}"
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>YAML equivalent</summary>
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
url: http://example.com
|
|
||||||
requests: 1000
|
|
||||||
concurrency: 10
|
|
||||||
headers:
|
|
||||||
User-Agent: "{{ fakeit_UserAgent }}"
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
Send requests with random user data:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sarin -U http://example.com/api/users -r 1000 -c 10 \
|
|
||||||
-M POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-B '{"name": "{{ fakeit_Name }}", "email": "{{ fakeit_Email }}"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>YAML equivalent</summary>
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
url: http://example.com/api/users
|
|
||||||
requests: 1000
|
|
||||||
concurrency: 10
|
|
||||||
method: POST
|
|
||||||
headers:
|
|
||||||
Content-Type: application/json
|
|
||||||
body: '{"name": "{{ fakeit_Name }}", "email": "{{ fakeit_Email }}"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
Use values to share generated data across headers and body:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sarin -U http://example.com/api/users -r 1000 -c 10 \
|
|
||||||
-M POST \
|
|
||||||
-V "ID={{ fakeit_UUID }}" \
|
|
||||||
-H "X-Request-ID: {{ .Values.ID }}" \
|
|
||||||
-B '{"id": "{{ .Values.ID }}", "name": "{{ fakeit_Name }}"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>YAML equivalent</summary>
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
url: http://example.com/api/users
|
|
||||||
requests: 1000
|
|
||||||
concurrency: 10
|
|
||||||
method: POST
|
|
||||||
values: "ID={{ fakeit_UUID }}"
|
|
||||||
headers:
|
|
||||||
X-Request-ID: "{{ .Values.ID }}"
|
|
||||||
body: '{"id": "{{ .Values.ID }}", "name": "{{ fakeit_Name }}"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
Generate random IPs and timestamps:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sarin -U http://example.com/api/logs -r 500 -c 20 \
|
|
||||||
-M POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-B '{"ip": "{{ fakeit_IPv4Address }}", "timestamp": "{{ fakeit_Date }}", "action": "{{ fakeit_HackerVerb }}"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>YAML equivalent</summary>
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
url: http://example.com/api/logs
|
|
||||||
requests: 500
|
|
||||||
concurrency: 20
|
|
||||||
method: POST
|
|
||||||
headers:
|
|
||||||
Content-Type: application/json
|
|
||||||
body: '{"ip": "{{ fakeit_IPv4Address }}", "timestamp": "{{ fakeit_Date }}", "action": "{{ fakeit_HackerVerb }}"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
> For the complete list of 320+ template functions, see the **[Templating Guide](templating.md)**.
|
|
||||||
|
|
||||||
## Headers, Cookies, and Parameters
|
## Headers, Cookies, and Parameters
|
||||||
|
|
||||||
**Custom headers:**
|
**Custom headers:**
|
||||||
@@ -323,6 +225,140 @@ cookies:
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## Dynamic Requests with Templating
|
||||||
|
|
||||||
|
**Dynamic URL paths:**
|
||||||
|
|
||||||
|
Test different resource endpoints with random IDs:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U "http://example.com/users/{{ fakeit_UUID }}/profile" -r 1000 -c 10
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/users/{{ fakeit_UUID }}/profile
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Test with random numeric IDs:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U "http://example.com/products/{{ fakeit_Number 1 10000 }}" -r 1000 -c 10
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/products/{{ fakeit_Number 1 10000 }}
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
**Generate a random User-Agent for each request:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com -r 1000 -c 10 \
|
||||||
|
-H "User-Agent: {{ fakeit_UserAgent }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
headers:
|
||||||
|
User-Agent: "{{ fakeit_UserAgent }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Send requests with random user data:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com/api/users -r 1000 -c 10 \
|
||||||
|
-M POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-B '{"name": "{{ fakeit_Name }}", "email": "{{ fakeit_Email }}"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/api/users
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
body: '{"name": "{{ fakeit_Name }}", "email": "{{ fakeit_Email }}"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Use values to share generated data across headers and body:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com/api/users -r 1000 -c 10 \
|
||||||
|
-M POST \
|
||||||
|
-V "ID={{ fakeit_UUID }}" \
|
||||||
|
-H "X-Request-ID: {{ .Values.ID }}" \
|
||||||
|
-B '{"id": "{{ .Values.ID }}", "name": "{{ fakeit_Name }}"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/api/users
|
||||||
|
requests: 1000
|
||||||
|
concurrency: 10
|
||||||
|
method: POST
|
||||||
|
values: "ID={{ fakeit_UUID }}"
|
||||||
|
headers:
|
||||||
|
X-Request-ID: "{{ .Values.ID }}"
|
||||||
|
body: '{"id": "{{ .Values.ID }}", "name": "{{ fakeit_Name }}"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Generate random IPs and timestamps:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sarin -U http://example.com/api/logs -r 500 -c 20 \
|
||||||
|
-M POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-B '{"ip": "{{ fakeit_IPv4Address }}", "timestamp": "{{ fakeit_Date }}", "action": "{{ fakeit_HackerVerb }}"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML equivalent</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: http://example.com/api/logs
|
||||||
|
requests: 500
|
||||||
|
concurrency: 20
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
body: '{"ip": "{{ fakeit_IPv4Address }}", "timestamp": "{{ fakeit_Date }}", "action": "{{ fakeit_HackerVerb }}"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
> For the complete list of 320+ template functions, see the **[Templating Guide](templating.md)**.
|
||||||
|
|
||||||
## Request Bodies
|
## Request Bodies
|
||||||
|
|
||||||
**Simple JSON body:**
|
**Simple JSON body:**
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# Templating
|
# Templating
|
||||||
|
|
||||||
Sarin supports Go templates in methods, bodies, headers, params, cookies, and values.
|
Sarin supports Go templates in URL paths, methods, bodies, headers, params, cookies, and values.
|
||||||
|
|
||||||
|
> **Note:** Templating in URL host and scheme is not supported. Only the path portion of the URL can contain templates.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -13,7 +13,7 @@ require (
|
|||||||
github.com/valyala/fasthttp v1.69.0
|
github.com/valyala/fasthttp v1.69.0
|
||||||
go.aykhans.me/utils v1.0.7
|
go.aykhans.me/utils v1.0.7
|
||||||
go.yaml.in/yaml/v4 v4.0.0-rc.3
|
go.yaml.in/yaml/v4 v4.0.0-rc.3
|
||||||
golang.org/x/net v0.48.0
|
golang.org/x/net v0.49.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -50,6 +50,6 @@ require (
|
|||||||
github.com/yuin/goldmark v1.7.16 // indirect
|
github.com/yuin/goldmark v1.7.16 // indirect
|
||||||
github.com/yuin/goldmark-emoji v1.0.6 // indirect
|
github.com/yuin/goldmark-emoji v1.0.6 // indirect
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
golang.org/x/term v0.38.0 // indirect
|
golang.org/x/term v0.39.0 // indirect
|
||||||
golang.org/x/text v0.32.0 // indirect
|
golang.org/x/text v0.33.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
12
go.sum
12
go.sum
@@ -101,15 +101,15 @@ go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go=
|
|||||||
go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -180,6 +180,15 @@ func validateTemplateValues(values []string, funcMap template.FuncMap) []types.F
|
|||||||
return validationErrors
|
return validationErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateTemplateURLPath(urlPath string, funcMap template.FuncMap) []types.FieldValidationError {
|
||||||
|
if err := validateTemplateString(urlPath, funcMap); err != nil {
|
||||||
|
return []types.FieldValidationError{
|
||||||
|
types.NewFieldValidationError("URL.Path", urlPath, err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateTemplates(config *Config) []types.FieldValidationError {
|
func ValidateTemplates(config *Config) []types.FieldValidationError {
|
||||||
// Create template function map using the same functions as sarin package
|
// Create template function map using the same functions as sarin package
|
||||||
randSource := sarin.NewDefaultRandSource()
|
randSource := sarin.NewDefaultRandSource()
|
||||||
@@ -190,6 +199,11 @@ func ValidateTemplates(config *Config) []types.FieldValidationError {
|
|||||||
|
|
||||||
var allErrors []types.FieldValidationError
|
var allErrors []types.FieldValidationError
|
||||||
|
|
||||||
|
// Validate URL path
|
||||||
|
if config.URL != nil {
|
||||||
|
allErrors = append(allErrors, validateTemplateURLPath(config.URL.Path, funcMap)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Validate methods
|
// Validate methods
|
||||||
allErrors = append(allErrors, validateTemplateMethods(config.Methods, funcMap)...)
|
allErrors = append(allErrors, validateTemplateMethods(config.Methods, funcMap)...)
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ func NewRequestGenerator(
|
|||||||
localRand := rand.New(randSource)
|
localRand := rand.New(randSource)
|
||||||
templateFuncMap := NewDefaultTemplateFuncMap(randSource)
|
templateFuncMap := NewDefaultTemplateFuncMap(randSource)
|
||||||
|
|
||||||
|
pathGenerator, isPathGeneratorDynamic := createTemplateFunc(requestURL.Path, templateFuncMap)
|
||||||
methodGenerator, isMethodGeneratorDynamic := NewMethodGeneratorFunc(localRand, methods, templateFuncMap)
|
methodGenerator, isMethodGeneratorDynamic := NewMethodGeneratorFunc(localRand, methods, templateFuncMap)
|
||||||
paramsGenerator, isParamsGeneratorDynamic := NewParamsGeneratorFunc(localRand, params, templateFuncMap)
|
paramsGenerator, isParamsGeneratorDynamic := NewParamsGeneratorFunc(localRand, params, templateFuncMap)
|
||||||
headersGenerator, isHeadersGeneratorDynamic := NewHeadersGeneratorFunc(localRand, headers, templateFuncMap)
|
headersGenerator, isHeadersGeneratorDynamic := NewHeadersGeneratorFunc(localRand, headers, templateFuncMap)
|
||||||
@@ -51,35 +52,45 @@ func NewRequestGenerator(
|
|||||||
|
|
||||||
valuesGenerator := NewValuesGeneratorFunc(values, templateFuncMap)
|
valuesGenerator := NewValuesGeneratorFunc(values, templateFuncMap)
|
||||||
|
|
||||||
|
var (
|
||||||
|
data valuesData
|
||||||
|
path string
|
||||||
|
err error
|
||||||
|
)
|
||||||
return func(req *fasthttp.Request) error {
|
return func(req *fasthttp.Request) error {
|
||||||
req.SetRequestURI(requestURL.Path)
|
|
||||||
req.Header.SetHost(requestURL.Host)
|
req.Header.SetHost(requestURL.Host)
|
||||||
|
|
||||||
data, err := valuesGenerator()
|
data, err = valuesGenerator()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := methodGenerator(req, data); err != nil {
|
path, err = pathGenerator(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.SetRequestURI(path)
|
||||||
|
|
||||||
|
if err = methodGenerator(req, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyTemplateFuncMapData.ClearFormDataContenType()
|
bodyTemplateFuncMapData.ClearFormDataContenType()
|
||||||
if err := bodyGenerator(req, data); err != nil {
|
if err = bodyGenerator(req, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := headersGenerator(req, data); err != nil {
|
if err = headersGenerator(req, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if bodyTemplateFuncMapData.GetFormDataContenType() != "" {
|
if bodyTemplateFuncMapData.GetFormDataContenType() != "" {
|
||||||
req.Header.Add("Content-Type", bodyTemplateFuncMapData.GetFormDataContenType())
|
req.Header.Add("Content-Type", bodyTemplateFuncMapData.GetFormDataContenType())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := paramsGenerator(req, data); err != nil {
|
if err = paramsGenerator(req, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := cookiesGenerator(req, data); err != nil {
|
if err = cookiesGenerator(req, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +98,8 @@ func NewRequestGenerator(
|
|||||||
req.URI().SetScheme("https")
|
req.URI().SetScheme("https")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, isMethodGeneratorDynamic ||
|
}, isPathGeneratorDynamic ||
|
||||||
|
isMethodGeneratorDynamic ||
|
||||||
isParamsGeneratorDynamic ||
|
isParamsGeneratorDynamic ||
|
||||||
isHeadersGeneratorDynamic ||
|
isHeadersGeneratorDynamic ||
|
||||||
isCookiesGeneratorDynamic ||
|
isCookiesGeneratorDynamic ||
|
||||||
|
|||||||
Reference in New Issue
Block a user