Wiki
Download
Manual
Eggs
API
Tests
Bugs
show
edit
history
You can edit this page using
wiki syntax
for markup.
Article contents:
== http-curl HTTP client egg for CHICKEN Scheme based on libcurl, with robust HTTPS/TLS support and an API compatible with [[https://wiki.call-cc.org/eggref/5/http-client|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: <enscript highlight="bash"> brew install curl </enscript> On Debian/Ubuntu: <enscript highlight="bash"> apt install libcurl4-openssl-dev </enscript> === Installation <enscript highlight="bash"> chicken-install http-curl </enscript> From a local checkout: <enscript highlight="bash"> chicken-install </enscript> === Testing The default test suite runs without network access: <enscript highlight="bash"> csi -s tests/run.scm </enscript> Network-backed tests are opt-in: <enscript highlight="bash"> TEST_NETWORK=1 csi -s tests/run.scm </enscript> A separate stress/leak check is available for manual runs: <enscript highlight="bash"> TEST_NETWORK=1 LEAK_ITERATIONS=25 leaks --atExit -- csi -s tests/leak-check.scm </enscript> === Basic Usage <enscript highlight="scheme"> (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) </enscript> === API <procedure>(with-input-from-request uri-or-request writer reader)</procedure> 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. <enscript highlight="scheme"> (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) </enscript> <procedure>(call-with-input-request uri-or-request writer reader)</procedure> 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. <enscript highlight="scheme"> (call-with-input-request "https://httpbin.org/get" #f (lambda (port) (read-string #f port))) </enscript> <procedure>(call-with-input-request* uri-or-request writer reader)</procedure> 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. <enscript highlight="scheme"> (call-with-input-request* "https://httpbin.org/get" #f (lambda (port response) (printf "Status: ~A~%" (response-code response)) (read-string #f port))) </enscript> <procedure>(call-with-response request writer reader)</procedure> 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. <enscript highlight="scheme"> (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))))) </enscript> === Request Objects For custom methods, headers, and other request metadata, pass an intarweb request object instead of a URI string. <enscript highlight="scheme"> (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)) </enscript> === Parameters <parameter>(max-redirect-depth)</parameter> Maximum number of redirects to follow. The default is 5. <parameter>(max-retry-attempts)</parameter> Maximum retry attempts on failure. The default is 1. <parameter>(max-idle-connections)</parameter> Maximum idle connections setting kept for http-client API compatibility. libcurl manages connections internally. The default is 32. <parameter>(client-software)</parameter> User-Agent string sent with requests. The default is "http-curl/0.1". <parameter>(retry-request?)</parameter> Predicate hook kept for http-client API compatibility. The default accepts the request and returns #t. <parameter>(prepare-request)</parameter> Request transformation hook called before a request is sent. The default is {{default-prepare-request}}, which returns the request unchanged. <enscript highlight="scheme"> (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)) </enscript> <parameter>(determine-proxy)</parameter> Procedure used to determine the proxy for a URI. The default is {{determine-proxy-from-environment}}. <procedure>(determine-proxy-from-environment uri)</procedure> Returns the proxy from the {{https_proxy}} environment variable for HTTPS URIs, or {{http_proxy}} for other URI schemes. <parameter>(http-authenticators)</parameter> Authentication hook kept for http-client API compatibility. The default is the empty list. <parameter>(determine-username/password)</parameter> Authentication credential hook kept for http-client API compatibility. The default returns two #f values. <parameter>(determine-proxy-username/password)</parameter> Proxy authentication credential hook kept for http-client API compatibility. The default returns two #f values. <parameter>(server-connector)</parameter> 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)</procedure> No-op compatibility procedure. <procedure>(close-idle-connections! . args)</procedure> No-op compatibility procedure. <procedure>(store-cookie! . args)</procedure> No-op compatibility procedure. <procedure>(delete-cookie! . args)</procedure> No-op compatibility procedure. <procedure>(get-cookies-for-uri . args)</procedure> 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. <enscript highlight="scheme"> (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"))) </enscript> === Complete Examples ==== JSON POST <enscript highlight="scheme"> (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)) </enscript> ==== Inspect Response Status <enscript highlight="scheme"> (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))) </enscript> ==== 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. <enscript highlight="scheme"> (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)) </enscript> === 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. === Repository [[https://forgejo.rolando.cl/cpm/http-curl|Repository]]
Description of your changes:
I would like to authenticate
Authentication
Username:
Password:
Spam control
What do you get when you multiply 5 by 4?