zcurl
Make HTTP requests and keep WebSocket connections open directly in Zsh. zcurl brings libcurl into the shell as a builtin, retaining connections and response data without spawning a curl process for every call.
An evolving API: the current development version is intended for trying in projects. HTTP requests are synchronous; WebSocket progress is driven by explicit polling.
Keep the Connection in the Shell
Persistent HTTP connections
Reuse the connection pool across requests in the same shell. Request methods, bodies, and headers reset between calls.
Results in your shell
Read response bodies, headers, status codes, and timing from module parameters or copy them into your own associative array.
API request support
Send JSON or other literal request bodies, choose HTTP methods, and supply repeated headers. Raw response bytes retain NULs and trailing newlines.
Persistent WebSockets
Keep WS/WSS handles open, queue outgoing messages, and receive chunks through explicit bounded polling controlled by your application.
Verified TLS
Certificate and hostname verification stay enabled. Use a custom PEM trust file when needed; redirects are returned for the caller to handle.
Bounded responses
Set request and connection timeouts, cap response body size, and inspect structured error results when a transfer fails.
Build and Load
You need a C compiler, make, pkg-config, libcurl 8.16.0 or newer with WS/WSS support, and configured Zsh headers matching your shell. The preparation script downloads and configures pinned Zsh sources locally.
git clone https://github.com/ZaguanLabs/zcurl.git
cd zcurl
zsh scripts/prepare-zsh.zsh
make
make test
# In Zsh:
source "$PWD/zcurl.zsh"
zcurl --versionThe documented test environment uses Zsh 5.9.2 on Mageia x86_64. Preparation leaves your installed shell alone. Tests additionally need Python, OpenSSL, and curl; see the repository for the full prerequisites and ABI requirements.
A Response You Can Work With
Declare an associative array and pass its name with --result. Call the builtin directly, then inspect the result. Replace the example URL with your API endpoint:
typeset -A response
if zcurl --result response --fail \
--header 'Accept: application/json' \
-- https://api.example.com/items; then
print -r -- "HTTP $response[http_status], $response[bytes] bytes"
print -rn -- "$response[body]" > response.json
else
print -ru2 -- "Request failed: $response[error_kind]"
fiThe result snapshot survives later requests. With --fail, an HTTP error returns a nonzero status while preserving its response body for inspection.
Your Application Drives the Work
WebSocket handles support queued sends and incremental receives. Your event loop polls each handle to advance traffic and manage its lifetime. There is no autonomous background worker or concurrent HTTP request API.
Connections belong to the shell that loaded the module. Command substitution, background calls, and forked children inheriting the module are rejected. Use a fresh Zsh process when you need a separate owner.