You are looking at historical revision 45708 of this page. It may differ significantly from its current revision.
http-curl
HTTP client egg for CHICKEN Scheme based on libcurl, with robust HTTPS/TLS support and an API compatible with http-client.
Description
http-curl provides a small HTTP client interface backed by libcurl. It is intended as a drop-in replacement for common http-client usage where reliable HTTPS support is needed. Request and response objects use intarweb, URI handling uses uri-common, and response bodies are streamed through a port instead of being buffered entirely in memory.
Requirements
- CHICKEN Scheme 5.0 or later
- libcurl with development headers
- Dependencies: intarweb, uri-common, srfi-1, srfi-13, srfi-18
On macOS:
brew install curl
On Debian/Ubuntu:
apt install libcurl4-openssl-dev
Installation
chicken-install http-curl
From a local checkout:
chicken-install
Testing
The default test suite runs without network access:
csi -s tests/run.scm
Network-backed tests are opt-in:
TEST_NETWORK=1 csi -s tests/run.scm
A separate stress/leak check is available for manual runs:
TEST_NETWORK=1 LEAK_ITERATIONS=25 leaks --atExit -- csi -s tests/leak-check.scm
Basic Usage
(import http-curl (chicken io)) ;; Simple GET (with-input-from-request "https://example.com" #f read-string) ;; Form-encoded POST (with-input-from-request "https://httpbin.org/post" '((foo . "bar") (baz . "quux")) read-string)
API
[procedure] (with-input-from-request uri-or-request writer reader)Convenience interface for making a request and reading the response body from the current input port. The reader argument is a thunk called with the response body bound to current-input-port.
The uri-or-request argument may be a URI string, a uri-common URI object, or an intarweb request object.
The writer argument controls the request body:
- #f performs a GET when uri-or-request is not already a request object
- A string is sent as the request body
- An alist is form-url-encoded and sent as application/x-www-form-urlencoded
- A procedure writes the request body to current-output-port
Returns three values: the reader result, the request URI, and the intarweb response object.
(with-input-from-request "https://httpbin.org/get" #f read-string) (with-input-from-request "https://httpbin.org/post" '((name . "Ada") (language . "Scheme")) read-string)[procedure] (call-with-input-request uri-or-request writer reader)
Like with-input-from-request, but reader receives the response body port as its argument.
Returns three values: the reader result, the request URI, and the intarweb response object.
(call-with-input-request
"https://httpbin.org/get" #f
(lambda (port)
(read-string #f port)))[procedure] (call-with-input-request* uri-or-request writer reader)
Like call-with-input-request, but reader receives both the response body port and the intarweb response object.
Returns three values: the reader result, the request URI, and the intarweb response object.
(call-with-input-request*
"https://httpbin.org/get" #f
(lambda (port response)
(printf "Status: ~A~%" (response-code response))
(read-string #f port)))[procedure] (call-with-response request writer reader)
Low-level interface. The request argument must be an intarweb request object. The writer argument is called with an output port and should write the request body. The reader argument is called with the intarweb response object, whose port contains the response body.
Returns three values: the reader result, the request URI, and the intarweb response object.
(import intarweb uri-common) (let* ((uri (uri-reference "https://httpbin.org/post")) (headers (headers '((content-type #(application/json ()))))) (request (make-request uri: uri method: 'POST headers: headers))) (call-with-response request (lambda (out) (display "{\"key\":\"value\"}" out)) (lambda (response) (read-string #f (response-port response)))))
Request Objects
For custom methods, headers, and other request metadata, pass an intarweb request object instead of a URI string.
(import http-curl intarweb uri-common (chicken io)) (let* ((uri (uri-reference "https://httpbin.org/post")) (headers (headers '((content-type #(application/json ()))))) (request (make-request uri: uri method: 'POST headers: headers))) (with-input-from-request request "{\"test\":1}" read-string))
Parameters
[parameter] (max-redirect-depth)Maximum number of redirects to follow. The default is 5.
[parameter] (max-retry-attempts)Maximum retry attempts on failure. The default is 1.
[parameter] (max-idle-connections)Maximum idle connections setting kept for http-client API compatibility. libcurl manages connections internally. The default is 32.
[parameter] (client-software)User-Agent string sent with requests. The default is "http-curl/0.1".
[parameter] (retry-request?)Predicate hook kept for http-client API compatibility. The default accepts the request and returns #t.
[parameter] (prepare-request)Request transformation hook called before a request is sent. The default is default-prepare-request, which returns the request unchanged.
(import intarweb) (parameterize ((prepare-request (lambda (request) (update-request request headers: (headers '((accept #(application/json ()))) (request-headers request)))))) (with-input-from-request "https://example.com" #f read-string))[parameter] (determine-proxy)
Procedure used to determine the proxy for a URI. The default is determine-proxy-from-environment.
[procedure] (determine-proxy-from-environment uri)Returns the proxy from the https_proxy environment variable for HTTPS URIs, or http_proxy for other URI schemes.
[parameter] (http-authenticators)Authentication hook kept for http-client API compatibility. The default is the empty list.
[parameter] (determine-username/password)Authentication credential hook kept for http-client API compatibility. The default returns two #f values.
[parameter] (determine-proxy-username/password)Proxy authentication credential hook kept for http-client API compatibility. The default returns two #f values.
[parameter] (server-connector)Server connector hook kept for http-client API compatibility. The default is default-server-connector; libcurl handles connections internally.
Connection And Cookie Procedures
The following procedures are exported for http-client API compatibility. Connection handling and cookies are managed by libcurl in this implementation, so these are currently no-ops or empty results.
[procedure] (close-connection! . args)No-op compatibility procedure.
[procedure] (close-idle-connections! . args)No-op compatibility procedure.
[procedure] (store-cookie! . args)No-op compatibility procedure.
[procedure] (delete-cookie! . args)No-op compatibility procedure.
[procedure] (get-cookies-for-uri . args)Returns the empty list.
Error Conditions
Non-2xx responses raise CHICKEN composite conditions compatible with http-client condition names:
- (http client-error) for 4xx responses
- (http server-error) for 5xx responses
- (http unexpected-server-response) for other non-2xx responses
Each HTTP error condition includes response and body properties.
(import http-curl (chicken condition) (chicken io)) (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")))
Complete Examples
JSON POST
(import http-curl intarweb uri-common (chicken io)) (let* ((uri (uri-reference "https://httpbin.org/post")) (headers (headers '((content-type #(application/json ()))))) (request (make-request uri: uri method: 'POST headers: headers))) (with-input-from-request request "{\"key\":\"value\"}" read-string))
Inspect Response Status
(import http-curl (chicken format) (chicken io)) (call-with-input-request* "https://httpbin.org/get" #f (lambda (port response) (printf "Status: ~A~%" (response-code response)) (read-string #f port)))
Preparing Requests
Use prepare-request when you want to apply the same intarweb request transformation before requests are sent. The hook receives the request object and must return the request object to send.
(import http-curl intarweb (chicken io)) (define (prefer-json request) (update-request request headers: (headers '((accept #(application/json ()))) (request-headers request)))) (parameterize ((prepare-request prefer-json)) (with-input-from-request "https://httpbin.org/get" #f read-string))
Architecture
Response bodies stream through an OS pipe. libcurl runs in a pthread while Scheme code reads from the response body port in real time. The body is not fully buffered in Scheme memory before the reader runs.
License
Copyright (c) 2025 Rolando Abarca. Released under the BSD-3-Clause License.