Native NetworkingExperimentalC • Zsh • libcurl

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 --version

The 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]"
fi

The 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.