README
ΒΆ
π Gurl-cli
Gurl-cli is a high-performance, stateful HTTP, gRPC & WebSocket client designed for the command line.
Unlike standard tools (curl, postman, grpcurl, websocat), gurl-cli is engineered for request chaining and automated flow testing. It allows you to execute sequences of requests where the output of one (e.g., Auth Tokens, session cookies, generated UUIDs) automatically feeds into the next. Everything is defined in a single, readable file using a custom, zero-allocation configuration format (.gurlf) that completely eliminates JSON escaping hell.
πΊ Navigation
- β‘ Quickstart
- π Installation
- π Configuration Types
- π§© Dynamic Macros & Flow Control
- π§ͺ Integration Testing
- β οΈ Core Concepts & Constraints
- π Architecture & Performance
- π License
β‘ Quickstart
gurl-cli operates via a clean command-line interface, allowing you to run files or raw configurations directly.
# Run a specific configuration file
gurl-cli run config.gurlf
# Run a raw configuration directly from the terminal (no file required)
gurl-cli run "
[http_config]
URL:http://localhost:8080
Type:http
ID:0
[\http_config]"
# Create a template or get help
gurl-cli create config.gurlf http
gurl-cli help
π Installation
Ensure you have Go installed, then run:
go install https://github.com/Votline/Gurl-cli@latest
Or download from github Releases
π Configuration Types
Configs use the custom .gurlf syntax. The tool writes responses back into the configuration file automatically, making debugging instant.
1. HTTP (Stateful Requests)
Standard HTTP requests support automatic cookie jars, headers, and body payloads.
[reg]
URL:http://localhost:8080/api/users/reg
Method:POST
Body:`
{
"name": "Viza",
"email": "some@mail.com",
"role":"admin",
"password":"eightpswd"
}
`
Headers:Content-Type: application/json
ID:0
Type:http
[\reg]
2. gRPC (First-Class Support)
Easily test your microservices by pointing directly to your .proto files or using reflection inside the grpc of your servers.
[new_course]
Target:localhost:50052
Endpoint:courses.CoursesService/NewCourse
Data:`
{
"user_id": "12345",
"name": "some_name",
"description": "cool desc",
"price": "1347"
}
`
ProtoPath:../protos/courses.proto
ID:0
Type:grpc
[\new_course]
3. WebSockets (Real-Time Flows)
Full support for interactive WebSocket connections natively through the HTTP type.
- Use
ws://to send a single configured payload. - Use
while:ws://to open an interactive session and stream messages directly from your terminal'sstdin.
[chat_session]
URL:while:ws://localhost:8080/ws
ID:1
Type:http
[\chat_session]
4. Repeat (DRY Principle)
Don't rewrite identical payloads. Inherit from a previous request using TargetID and patch only the fields you need using Replace.
[log_upd_user]
TargetID:1
Replace:`
[rep]
Body:`
{
"name": "a62893bc-70af-4fb3-aca6-b663ad35404f",
"email": "upd@mail.com",
"password": "updatepaswd"
}`
[\rep]
`
ID:8
Type:repeat
[\log_upd_user]
5. Import (Modular Configs)
Keep your configs clean by importing base templates or fallback configurations. Variables can be passed down to the imported scope.
[import_config]
TargetPath:fallback.gurlf
ID:2
Type:import
SetVariables:`
[vars]
UserID: FALLBACK {RANDOM oneof=uuid}
[\vars]
`
[\import_config]
π§© Dynamic Macros & Flow Control
gurl-cli becomes powerful when you link requests together using in-place macros.
1. Response & Cookie Injection
Extract data from previous requests or stateful fields:
- JSON Extraction:
{RESPONSE id=1 json:token}- Extracts a field from the response body of configID:1. - Stateful Cookies: Use the
CookieInfield to inject session data:{COOKIES id=1}- Injects all cookies captured in theCookieOutfield of configID:1.{COOKIES id=file}- Tells the transport to use cookies defined directly within the currentCookieInblock.
2. Environment Integration
Manage state across different .gurlf files or system sessions. This is the heavy lifting for cross-file communication.
- Save State: Use
SetEnvironmentsto persist values to a local.env_tempfile or OS env. - Retrieve State:
{ENVIRONMENT key=Token ; from=.env_temp}or{ENVIRONMENT key=USER ; from=os}. - Defaults: You can now provide fallback values using
; default=...if the environment or variable is missing.
Example
[config] SetEnvironments:` [envs] <- there is no such file, the environment will be in-memory. OsEnvName:os env {RANDOM oneof=uuid} [\envs] [.env] <- this file exists. The old values will be overwritten by the new ones in case of a collision. FileEnvName:file env {RANDOM oneof=uuid} [\.env] ` Body:` { "host": "{ENVIRONMENT key=DB_HOST ; from=os ; default=localhost}" <- will be used 'localhost' "osname": "{ENVIRONMENT key=OsEnvName ; from=os ; default=nop}" <- will be used value from os.GetEnv "filename": "{ENVIRONMENT key=FileEnvName ; from=.env ; default=nop}" <- will be used value from file '.env' } ` [\config]
3. Variables Integration
Variables are designed for in-memory state management during a single execution. They do not persist to disk.
- In-Place Usage:
{VARIABLE key=Name ; default=guest}- Get a variable with a fallback. - Import config Pattern: The primary use case is passing data into import configs.
Example
[config] TargetPath: fallback.gurlf SetVariables:` [vars] UserID: FALLBACK - {RANDOM oneof=uuid} [\vars] ` Type:import [\config]Note:
fallback.gurlfwill now be able to resolve{VARIABLE key=UserID}.
4. Smart Randomization
Generate dynamic data in 0 allocs/op:
{RANDOM oneof=uuid}- High-speed UUID.{RANDOM oneof=user,admin}- Random pick from a list.{RANDOM oneof=int(10,100)}- Random integer in range.{RANDOM oneof=int}- Random integer.
5. Flow Control & Failures
- Wait:
Wait: 5s- Delays execution (supportsms, s, m, h). - Timeout:
Timeout: 15s- Sets a dynamic context deadline for the request (stops hanging connections). - Expect: Define success criteria and branching logic:
Expect: 200;fail=crash- Hard stop on failure.Expect: 200;fail=5- If not 200, jump to configID:5and stop.Expect: 0- (gRPC) ExpectsOKstatus.
6. Request settings
- The
IgnoreCertField: AddingIgnoreCert: trueto a config (or inherited via repeat) will skip TLS/SSL verification for that request. Usefalseor omit to enforce secure connections. - Context Timeouts: The
Timeoutfield sets a timeout for the context. Automatically inherited by childrepeatconfigs.
π§ͺ Integration Testing
Because gurl-cli persists its state (Responses, Cookies, Envs) back into files, it is perfect for complex integration scenarios.
Scenario: Cross-file state persistence
You can run a chain of requests where file2.gurlf depends on the output of file1.gurlf.
Step 1: auth.gurlf
Captures cookies and saves the UserID to a temporary environment.
[login]
URL: http://localhost:8080/login
SetEnvironments: `
[.env_temp]
UserID: {RESPONSE id=0 json:id}
Cookies: "{COOKIES id=0}"
[\.env_temp]
`
ID: 0
Type: http
[\login]
Step 2: delete_account.gurlf
Uses the previously saved cookies and ID.
[del]
URL: http://localhost:8080/user/{ENVIRONMENT key=UserID ; from=.env_temp}
CookieIn: `
{COOKIES id=file}
{ENVIRONMENT key=Cookies ; from=.env_temp}
`
Type: http
[\del]
Step 3: check.gurlf
Combine two files into one for easy startup.
[import_login]
TargetPath:auth.gurlf
ID:0
Type:import
[\import_login]
[import_delete]
TargetPath:delete_account.gurlf
ID:1
Type:import
[\import_delete]
Step 4: run it
gcli run check.gurlf
β Core Concepts & Constraints
1. The Backtick & Newline Rule (Nesting)
The .gurlf parser uses a high-speed, zero-allocation scanning logic. It identifies the end of a multi-line field by looking for a backtick strictly surrounded by newlines (\n + ` + \n).
[!CAUTION] A backtick on a new line always terminates the top-level block. When nesting configs, ensure the inner backticks do not mimic this pattern.
- CORRECT: Keep the inner closing backtick on the same line as your data.
- INCORRECT: Putting the inner closing backtick on a new line will break the outer parser.
| Syntax | Result |
|---|---|
Body: `value` |
One-line string. Value is value |
Body: "value\n" |
Multi-line string. Values is "value\n" |
Replace: `[patch]\n Body:`data`\n[\patch]\n`\n |
Nested Correct. The last backtick is surrounded by \n, which means the end of the Replace key value |
Replace: `[patch]\n Body:`data\n`\n [\patch]\n`\n |
Nested Incorrect. There is a \n\n` in the value, which means the end of the value for the 'Replace' field, but this is not the case. |
[!NOTE] Correct config:
[config] GlobalKey:` [inner_cfg] LocalKey:` LocalValue` <- end of 'LocalKey'. Syntax: '`\n' [\inner_cfg] ` <- end of 'GlobalKey'. Syntax: '\n`\n' [config]
2. Configuration Field Rules
- The
ReplaceField: Strictly exclusive to therepeatconfiguration type. Used to patch fields from aTargetID. - gRPC Reflection vs. ProtoPath: Omit
ProtoPathto use Server Reflection. If provided, it parses the local.protofile.
3. Syntax Upgrades
Macros now use ; as a separator to allow for clean default fallbacks (e.g., {ENVIRONMENT key=API_KEY ; from=os ; default=12345}). You can escape semicolons inside values if needed.
π Architecture & Performance
gurl-cli is explicitly engineered for low latency and zero-allocation execution, making it viable for high-throughput testing and microservice orchestration. The core architecture relies on aggressive object pooling and deep Go runtime optimizations.
1. Pre-allocation & Interface Hacking
Interface Hacking & Pre-allocation: To avoid heap allocations, the config package uses pre-allocated pools (10 objects per type). The parser peeks at the Type field and uses unsafe.Pointer to manually swap itab and data pointers.
2. Lock-Free Pipeline (Ring Buffers)
Lock-Free Pipeline: Data flows between the Parser, Core, File Writer and Console Writer via isolated, capped Ring Buffers. This decoupled architecture ensures the executor never blocks on the parser or disk I/O.
3. Optimized Custom Tooling:
Standard library functions (bytes.Split, json, strconv) are replaced with custom, allocation-free iterators. fastUUID and manual byte-slice scanning keep the hot path garbage-free.
4. Resilient Disk I/O:
The file writer uses a detached seek/flush mechanism to inject responses directly into .gurlf files. This context-free operation prevents file corruption even during abrupt process termination.
β‘ Benchmarks
Tested on AMD Ryzen 7 5800U (linux/amd64). The hot paths are garbage-free.
| Component / Function | Time (ns/op) | Allocations | Notes |
|---|---|---|---|
| ParseStream | ~362 ns/op | 0 allocs/op | Full multi-line config streaming. |
| HandleType | ~274 ns/op | 0 allocs/op | unsafe interface casting + mapping. |
| HandleInstructions | ~106 ns/op | 0 allocs/op | Finds macros (aka instructions) |
| ParseFindConfig | ~317 ns/op | 0 allocs/op | Finds config by target id, anmarshall it (import configs logic) |
| fastExtract | ~12.6 ns/op | 0 allocs/op | Rapid field extraction. |
| fastUUID | ~48 ns/op | 0 allocs/op | Generate UUID (libraries allocate memory) |
for 0 alloc in ParseStream, you need to comment out log.Debug
π License
Documentation
ΒΆ
There is no documentation for this package.
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
internal
|
|
|
buffer
Package buffer buffer.go contains ringbuffer implementation.
|
Package buffer buffer.go contains ringbuffer implementation. |
|
config
Package config create.go contains functions for create config.
|
Package config create.go contains functions for create config. |
|
core
Package core core.go contains core functions.
|
Package core core.go contains core functions. |
|
parser
Package parser fields.go parse config fields.
|
Package parser fields.go parse config fields. |
|
transport
Package transport grpc.go implemented gRPC requests.
|
Package transport grpc.go implemented gRPC requests. |