# http-curl HTTP client egg for CHICKEN Scheme based on libcurl. Provides robust HTTPS/TLS support out of the box with an API compatible with [http-client](https://wiki.call-cc.org/eggref/5/http-client). ## Requirements - CHICKEN 5 - libcurl (with development headers) On macOS: `brew install curl` On Debian/Ubuntu: `apt install libcurl4-openssl-dev` ## Installation From the egg directory: ``` chicken-install ``` ## Testing The default test suite runs offline: ```sh csi -s tests/run.scm ``` Network-backed tests are opt-in: ```sh TEST_NETWORK=1 csi -s tests/run.scm ``` A separate stress/leak check is available for manual runs: ```sh TEST_NETWORK=1 LEAK_ITERATIONS=25 leaks --atExit -- csi -s tests/leak-check.scm ``` ## Usage ```scheme (import http-curl (chicken io)) ;; Simple GET (with-input-from-request "https://example.com" #f read-string) ;; POST with JSON body (import intarweb uri-common) (let* ((uri (uri-reference "https://httpbin.org/post")) (h (headers '((content-type #(application/json ()))))) (req (make-request uri: uri method: 'POST headers: h))) (with-input-from-request req "{\"key\":\"value\"}" read-string)) ;; Form-encoded POST (with-input-from-request "https://httpbin.org/post" '((foo . "bar") (baz . "quux")) read-string) ;; Access response object (call-with-input-request* "https://httpbin.org/get" #f (lambda (port response) (printf "Status: ~A~%" (response-code response)) (read-string #f port))) ;; Error handling (import (chicken condition)) (condition-case (with-input-from-request "https://httpbin.org/status/404" #f read-string) ((http client-error) (print "got a 4xx error")) ((http server-error) (print "got a 5xx error"))) ``` ## API ### Procedures **`(with-input-from-request uri-or-request writer reader)`** The most convenient form. `reader` is a thunk that reads from `current-input-port`. `writer` can be `#f` (GET), a string (direct body), an alist (form-encoded POST), or a thunk that writes to `current-output-port`. Returns three values: `reader-result`, `uri`, `response`. **`(call-with-input-request uri-or-request writer reader)`** Like `with-input-from-request`, but `reader` receives the port as its argument: `(lambda (port) ...)`. **`(call-with-input-request* uri-or-request writer reader)`** Like `call-with-input-request`, but `reader` receives both port and response: `(lambda (port response) ...)`. **`(call-with-response request writer reader)`** Low-level interface. `request` is an intarweb request object. `writer` is `(lambda (output-port) ...)`. `reader` is `(lambda (response) ...)`. ### Parameters | Parameter | Default | Description | |-----------|---------|-------------| | `max-redirect-depth` | 5 | Maximum number of redirects to follow | | `max-retry-attempts` | 1 | Maximum retry attempts on failure | | `client-software` | `"http-curl/0.1"` | User-Agent string | | `prepare-request` | identity | Request transform hook | | `determine-proxy` | env-based | Proxy resolution (`http_proxy`/`https_proxy`) | ### Error conditions HTTP errors are raised as composite conditions matching http-client's format: - `(http client-error)` — 4xx responses - `(http server-error)` — 5xx responses - `(http unexpected-server-response)` — other non-2xx responses Each condition has `response` and `body` properties accessible via `condition-property-accessor`. ## Architecture Response bodies stream through an OS pipe. libcurl runs in a pthread; the Scheme reader reads from the pipe fd in real-time. The body is never fully buffered in memory. ## License BSD-3-Clause. See [LICENSE](LICENSE).