Lcl · Core · Packages · Libraries
HTTP client bindings for Lcl using libcurl.
0.1.0 note:
lcl-curlis built and used in-tree only; it is not part of thecmake --installartifact. The package vendors libcurl viaFetchContent, and a shipping decision for vendored deps is deferred. Use it by building Lcl with-DLCL_BUILD_CURL=ONand consuminglcl_curldirectly from your own CMake build.
libcurl4-openssl-dev on
Debian/Ubuntu, curl via Homebrew on macOS)cmake -S . -B build -DLCL_BUILD_CURL=ON
cmake --build buildCurl::init
let c [Curl::new]
Curl::set_url $c "https://api.example.com/data"
Curl::set_verb $c GET
Curl::set_header $c "Authorization: Bearer token123"
Curl::set_write_callback $c [lambda {chunk} {
puts "Received: $chunk"
}]
Curl::perform $c
let status [Curl::get_response_code $c]
puts "Status: $status"The model is libcurl’s easy interface, one proc per option: a handle
from Curl::new is an
opaque value, set_* procs configure it, Curl::perform runs the
transfer synchronously, and get_* procs read information
about the last transfer. Response bytes only reach your script through a
callback – without one libcurl writes the body to stdout.
A handle is released when its value is dropped. Every proc in this
namespace raises an error with an empty message when it fails
(wrong argument count, first argument not a curl handle, non-callable
callback, libcurl rejecting an option, or a failed transfer); use Curl::get_last_error
and Curl::error_string
for the reason after Curl::perform.
Curl::set_sse_callback
parses the response stream as SSE: events are split on blank lines,
: comment lines are ignored, and each event is delivered as
a dict:
| Key | Value |
|---|---|
event |
event type; "message" when the stream gives none |
data |
data lines joined with \n (absent when there were
none) |
id |
the id: field, when present |
retry |
the retry: field as an integer, when present |
Curl::set_sse_callback $c [lambda {event} {
let type [get $event event]
let data [get $event data ""]
puts "Event: $type, Data: $data"
}]An SSE stream typically never ends, so give the handle a Curl::set_timeout_ms
and treat Curl::is_timeout as
the normal way out:
catch {Curl::perform $c}
if [Curl::is_timeout $c] {
puts "Request timed out (expected for SSE)"
} else {
puts "Error: [Curl::error_string $c]"
}packages/lcl-curl/test/test.lcl runs under
ctest -R lcl-curl. The request cases talk to a local
jmalloc/echo-server from
lib/curl-dsl/docker-compose.yml (port 8080; it echoes
requests and streams Server-Sent Events at /.sse):
docker compose -f lib/curl-dsl/docker-compose.yml up -d
ctest --test-dir build -R lcl-curl
docker compose -f lib/curl-dsl/docker-compose.yml downWithout it those cases SKIP (visible in the output,
still a passing run). LCL_CURL_REQUIRE_SERVER=1 makes an
unreachable server fail the run instead (CI sets this on the Linux
rows); LCL_CURL_TEST_URL overrides the base URL (default
http://localhost:8080). None of the tests leave the
machine.
CurlProcs are listed in groups: lifecycle, request, timeouts and connection, protocol, redirects, authentication, TLS, proxy, DNS and connection reuse, speed limits, cookies, observability, callbacks, response information, error handling.
Setters. Each set_* proc takes the
handle and one value and returns nothing. Integer options
(CURLOPT_* long options) take an integer; boolean options
are 0/1. Passing something that is not a
handle, or a non-integer to an integer option, is an error.
Callbacks. A callback is any callable (a
lambda, a proc value) taking one argument. The handle keeps
a reference to it until it is replaced, the handle is reset, or the
handle is freed. Errors raised inside a callback do not abort the
transfer. Curl::set_write_callback
and Curl::set_sse_callback
install the same underlying libcurl write function, so the one set last
wins.
Getters. The get_* procs read
curl_easy_getinfo fields for the last transfer on the
handle; before any Curl::perform they
return zero or the empty string. Curl::get_last_error,
Curl::is_timeout
and Curl::error_string
read the result code Curl::perform recorded,
0 on a fresh handle.
Examples:
>> Curl::set_url "not a handle" "http://localhost/"
!!
>> Curl::set_timeout_ms [Curl::new] "soon"
!!
>> Curl::set_write_callback [Curl::new] "not callable"
!!
>> Curl::get_response_code [Curl::new]
0Curl::initInitialize libcurl globally (curl_global_init); call
once before anything else.
Arguments are ignored. Fails only if libcurl’s global initialization fails.
Examples:
>> Curl::init
""Curl::newCreate a new easy handle with default options.
Returns an opaque value; the handle, its header list and any callbacks are freed when the value is dropped. Takes no arguments.
Examples:
>> type [Curl::new]
"opaque"
>> Curl::new 1
!!Curl::reset handleReset handle to its initial state and drop its
callbacks.
Every option set on the handle is cleared
(curl_easy_reset), the write, header and SSE callbacks are
released, and any buffered partial SSE event is discarded. Headers added
with Curl::set_header
before the reset stay queued for the next transfer.
Examples:
>> let c [Curl::new]
>> Curl::set_url $c "http://localhost:8080/test"
>> Curl::reset $c
>> Curl::get_effective_url $c
""Curl::perform handleRun the configured transfer synchronously.
Blocks until the transfer finishes, invoking the write, header or SSE
callback as data arrives. The result code is stored for Curl::get_last_error,
Curl::is_timeout
and Curl::error_string;
any code other than CURLE_OK raises an error (with an empty
message). The header list built by Curl::set_header is
consumed by the call: add headers again before performing the same
handle a second time.
if [catch {Curl::perform $c}] {
puts "failed: [Curl::error_string $c]"
}Curl::set_url handle urlSet the request URL (CURLOPT_URL).
Examples:
>> let c [Curl::new]
>> Curl::set_url $c "http://localhost:8080/test"
>> Curl::get_effective_url $c
"http://localhost:8080/test"Curl::set_verb handle methodSet the request method string (CURLOPT_CUSTOMREQUEST):
GET, POST, PUT, …
This only changes the verb sent on the request line. Set a body with
Curl::set_body to
send one; use Curl::set_nobody for
a HEAD-style request.
Curl::set_header handle header *moreAppend one or more request headers, each a full
Name: value line.
Headers accumulate on the handle and are all sent by the next Curl::perform, which
then clears the list.
Curl::set_header $c "Content-Type: application/json" "Accept: application/json"Curl::set_body handle dataSet the request body (CURLOPT_COPYPOSTFIELDS); libcurl
copies the data.
Setting a body makes libcurl send a POST unless Curl::set_verb
overrides the method.
Curl::set_nobody handle flagSet CURLOPT_NOBODY (1 to skip the response
body, as for HEAD).
Curl::set_user_agent handle uaSet the User-Agent header
(CURLOPT_USERAGENT).
Curl::set_timeout_ms handle msMaximum total time for the transfer in milliseconds
(CURLOPT_TIMEOUT_MS).
Hitting it makes Curl::perform fail with
CURLE_OPERATION_TIMEDOUT (28), which Curl::is_timeout
reports.
Curl::set_accept_timeout_ms handle msAccept timeout for active FTP in milliseconds
(CURLOPT_ACCEPTTIMEOUT_MS).
Curl::set_connection_timeout_ms handle msConnection-phase timeout in milliseconds
(CURLOPT_CONNECTTIMEOUT_MS).
Curl::set_expect_100_timeout_ms handle msHow long to wait for a 100 Continue in milliseconds
(CURLOPT_EXPECT_100_TIMEOUT_MS).
Curl::set_interface handle nameOutgoing network interface name or address
(CURLOPT_INTERFACE).
Curl::set_low_speed_limit handle bytesLow-speed threshold in bytes per second
(CURLOPT_LOW_SPEED_LIMIT).
Together with Curl::set_low_speed_time:
abort when the transfer stays below this rate for that many seconds.
Curl::set_low_speed_time handle secsSeconds a transfer may stay below the low-speed limit
(CURLOPT_LOW_SPEED_TIME).
Curl::set_tcp_keep_alive handle flagEnable TCP keep-alive probes (CURLOPT_TCP_KEEPALIVE,
0/1).
Curl::set_tcp_keep_idle handle secsIdle seconds before the first keep-alive probe
(CURLOPT_TCP_KEEPIDLE).
Curl::set_tcp_keep_intvl handle secsSeconds between keep-alive probes
(CURLOPT_TCP_KEEPINTVL).
Curl::set_accept_encoding handle encodingAccept-Encoding to request and decode automatically
(CURLOPT_ACCEPT_ENCODING), e.g. "" for all
supported.
Curl::set_http_version handle versionPreferred HTTP version as a libcurl CURL_HTTP_VERSION_*
integer (CURLOPT_HTTP_VERSION).
Curl::set_follow_location handle flagFollow Location: redirects
(CURLOPT_FOLLOWLOCATION,
0/1).
Curl::set_max_redirects handle nMaximum number of redirects to follow
(CURLOPT_MAXREDIRS); -1 for unlimited.
Curl::set_post_redirect handle maskWhich redirects keep the POST method, as a
CURL_REDIR_POST_* bitmask
(CURLOPT_POSTREDIR).
Curl::set_httpauth handle maskHTTP authentication methods as a CURLAUTH_* bitmask
(CURLOPT_HTTPAUTH).
Curl::set_username handle userUser name for HTTP authentication
(CURLOPT_USERNAME).
Curl::set_password handle passPassword for HTTP authentication (CURLOPT_PASSWORD).
Curl::set_xoauth2_bearer handle tokenOAuth 2.0 bearer token (CURLOPT_XOAUTH2_BEARER).
Curl::set_ssl_verify_peer handle flagVerify the peer’s certificate (CURLOPT_SSL_VERIFYPEER,
0/1).
Curl::set_ssl_verify_host handle levelVerify that the certificate matches the host name
(CURLOPT_SSL_VERIFYHOST; libcurl expects 2 to
verify, 0 to skip).
Curl::set_ca_info handle pathPath to a CA certificate bundle file
(CURLOPT_CAINFO).
Curl::set_ca_path handle pathDirectory of CA certificates (CURLOPT_CAPATH).
Curl::set_ssl_cert handle pathClient certificate file (CURLOPT_SSLCERT).
Curl::set_ssl_cert_type handle typeClient certificate format, e.g. PEM or DER
(CURLOPT_SSLCERTTYPE).
Curl::set_ssl_key handle pathClient private key file (CURLOPT_SSLKEY).
Curl::set_ssl_key_type handle typeClient private key format, e.g. PEM or DER
(CURLOPT_SSLKEYTYPE).
Curl::set_key_password handle passPassphrase for the client private key
(CURLOPT_KEYPASSWD).
Curl::set_ssl_version handle versionPreferred TLS version as a CURL_SSLVERSION_* integer
(CURLOPT_SSLVERSION).
Curl::set_ssl_cipher_list handle listCipher list for TLS 1.2 and below, in the TLS backend’s syntax
(CURLOPT_SSL_CIPHER_LIST).
Curl::set_tls13_ciphers handle listCipher suites for TLS 1.3 (CURLOPT_TLS13_CIPHERS).
Curl::set_proxy handle urlProxy to use, as a URL or host:port
(CURLOPT_PROXY).
Curl::set_proxy_port handle portProxy port, overriding the one in the proxy string
(CURLOPT_PROXYPORT).
Curl::set_proxy_type handle typeProxy type as a CURLPROXY_* integer
(CURLOPT_PROXYTYPE).
Curl::set_proxy_username handle userUser name for proxy authentication
(CURLOPT_PROXYUSERNAME).
Curl::set_proxy_password handle passPassword for proxy authentication
(CURLOPT_PROXYPASSWORD).
Curl::set_no_proxy handle hostsComma-separated hosts that bypass the proxy
(CURLOPT_NOPROXY).
Curl::set_proxy_ssl_verify_peer handle flagVerify the HTTPS proxy’s certificate
(CURLOPT_PROXY_SSL_VERIFYPEER,
0/1).
Curl::set_proxy_ssl_verify_host handle levelVerify the HTTPS proxy’s certificate host name
(CURLOPT_PROXY_SSL_VERIFYHOST; 2 to verify,
0 to skip).
Curl::set_proxy_ca_info handle pathCA bundle file for the HTTPS proxy
(CURLOPT_PROXY_CAINFO).
Curl::set_proxy_ca_path handle pathCA directory for the HTTPS proxy
(CURLOPT_PROXY_CAPATH).
Curl::set_proxy_ssl_version handle versionTLS version for the HTTPS proxy as a CURL_SSLVERSION_*
integer (CURLOPT_PROXY_SSLVERSION).
Curl::set_doh_url handle urlDNS-over-HTTPS resolver URL (CURLOPT_DOH_URL).
Curl::set_dns_servers handle serversComma-separated DNS servers to use, host:port each
(CURLOPT_DNS_SERVERS; needs a c-ares build of libcurl).
Curl::set_fresh_connect handle flagForce a new connection instead of a cached one
(CURLOPT_FRESH_CONNECT, 0/1).
Curl::set_forbid_reuse handle flagClose the connection after this transfer instead of caching it
(CURLOPT_FORBID_REUSE, 0/1).
Curl::set_max_recv_speed handle bytesCap the download rate in bytes per second
(CURLOPT_MAX_RECV_SPEED_LARGE).
Curl::set_max_send_speed handle bytesCap the upload rate in bytes per second
(CURLOPT_MAX_SEND_SPEED_LARGE).
Curl::set_cookie_file handle pathCookie file to read at start, or "" to enable the cookie
engine with no file (CURLOPT_COOKIEFILE).
Curl::set_cookie_jar handle pathFile to write cookies to when the handle is freed
(CURLOPT_COOKIEJAR).
Curl::set_verbose handle flagPrint libcurl’s diagnostic trace to stderr
(CURLOPT_VERBOSE, 0/1).
Curl::set_include_header handle flagInclude response headers in the body data given to the write callback
(CURLOPT_HEADER, 0/1).
Curl::set_write_callback handle procCall proc with each chunk of the response body as it
arrives.
Chunks are libcurl’s write-function buffers (up to 16 KiB), delivered as strings; concatenate them for the full body.
var body {}
Curl::set_write_callback $c [lambda {chunk} {
set! body "$body$chunk"
}]Curl::set_header_callback handle procCall proc with each response header line, including its
trailing CRLF.
The status line and the blank line ending the header block are delivered too, once per response (so again after a redirect).
Curl::set_sse_callback handle procParse the response as Server-Sent Events and call proc
with each event dict.
See Server-Sent Events above for the dict layout. Events are
dispatched as soon as their terminating blank line arrives; a trailing
partial event is kept across chunks and dropped on Curl::reset.
Curl::get_response_code handleHTTP status code of the last response
(CURLINFO_RESPONSE_CODE), 0 if none.
Curl::get_content_type handleContent-Type of the last response
(CURLINFO_CONTENT_TYPE), "" if none.
Examples:
>> Curl::get_content_type [Curl::new]
""Curl::get_effective_url handleFinal URL after redirects (CURLINFO_EFFECTIVE_URL); the
configured URL before a transfer.
Curl::get_total_time handleTotal transfer time in seconds as a float
(CURLINFO_TOTAL_TIME).
The value is narrowed to single precision on the way out.
Curl::get_header_size handleSize in bytes of all headers received
(CURLINFO_HEADER_SIZE).
Curl::get_request_size handleSize in bytes of the request sent
(CURLINFO_REQUEST_SIZE).
Curl::get_num_connects handleNumber of new connections the transfer had to make
(CURLINFO_NUM_CONNECTS).
Curl::get_primary_ip handleIP address of the server used for the last connection
(CURLINFO_PRIMARY_IP).
Examples:
>> Curl::get_primary_ip [Curl::new]
""Curl::get_primary_port handlePort of the server used for the last connection
(CURLINFO_PRIMARY_PORT).
Curl::get_last_error handleCURLcode of the last Curl::perform as an
integer (0 = OK, 28 = timeout).
Examples:
>> Curl::get_last_error [Curl::new]
0Curl::is_timeout handleReturn 1 if the last transfer ended with
CURLE_OPERATION_TIMEDOUT, else 0.
Examples:
>> Curl::is_timeout [Curl::new]
0Curl::error_string handleHuman-readable text for the last result code
(curl_easy_strerror).
Examples:
>> Curl::error_string [Curl::new]
"No error"